Springboot注入成员变量HttpServletRequest的原理分析

 更新时间:2024年05月29日 09:20:51   作者:fenglllle  
这篇文章主要介绍了Springboot注入成员变量HttpServletRequest的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

前言

最近做项目,springboot项目,本来我们在controller的requestmapping取参数值或者返回写时,使用方法参数,但是发现老项目直接注入了成员变量,Spring本身是单例的,如果是成员变量注入,那么也是单例的,怎么实现不同的请求读取不同的参数呢,如果实现线程安全呢,笔者立马想到了ThreadLocal,但是如果要说就是这个原理,那么必须源码证明。

准备demo

简单写一个demo

@RestController
public class DemoController {
 
    @Autowired
    private HttpServletRequest request;
 
    @GetMapping("/hello")
    public String demo(String param) {
        request.getParameterMap().forEach((k,v)-> System.out.println(k + " : " + Arrays.toString(v)));
        return param + ":hello";
    }
}

只写了 HttpServletRequest,实际上HttpServletResponse亦是如此。

分析demo,注入的HttpServletRequest是接口类型,那么在Boot启动中就会动态代理实现,由于是接口,可以推测是JDK动态代理,debug果然如此。

源码分析

根据debug,JDK动态代理注入了接口的实现类,关键在于InvocationHandler

Spring使用如下:

org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler

里面有关键代码

return method.invoke(this.objectFactory.getObject(), args);

this.objectFactory.getObject()这句决定线程安全

看看Spring Bean下JDK是怎么动态代理注入的

可以看到JDK动态代理在Spring注入的时候,把这个factory注入了InvocationHandler

其中的handler的invoke方法,这里实际上还要其他类的读取埋点。

这里的invoke仅仅是反射,关键还是 HttpServletRequest的对象来源,跟踪读取逻辑

org.springframework.web.context.request.RequestContextHolder

到此就明确了,注入的成员变量,动态代理后使用Threadlocal处理,所以线程安全,因为每次请求都是线程请求,那么原始的HttpServletRequest对象怎么塞进去的呢,就要看filter的了

org.springframework.web.filter.RequestContextFilter

在doFilter时,执行

同理在org.springframework.web.servlet.FrameworkServlet也会再次读取和写入

这里是为了,如果filter被去除的时候可以有值,再次保底,并且在结束时rest

只不过这个rest有点奇怪

对象并没有清除,还在,说明即使 FrameworkServlet后还可以获取,因为有filter可能会在这个后面执行,如果干掉,很可能就不能读取了。

会在RequestContextFilter后面remove;如果我们去掉这个filter,那么需要自定义一个filter实现remove防止内存泄漏。

清除当前线程及子线程ThreadLocal

public static void resetRequestAttributes() {
    requestAttributesHolder.remove();
    inheritableRequestAttributesHolder.remove();
}

至此 HttpServletRequest的成员变量注入逻辑,即ThreadLocal变量,所以请求可以正常访问

总结

实际上这种用法很多项目都用了,只不过我们写代码下意思的通过方法参数来规避线程安全性,这种想法是有益的,可以从源头规避风险,不过实际上Spring也帮我们考虑了这个问题,相当于使用RequestContextFilter做了保底措施。

源码分析实际上是知所以然,尤其是我们自己写公共SDK时,可以把这种设计放在代码中,实现优雅和保底逻辑。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java中建立0-10m的消息(字符串)实现方法

    java中建立0-10m的消息(字符串)实现方法

    下面小编就为大家带来一篇java中建立0-10m的消息(字符串)实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • java的时间类汇总(齐全)

    java的时间类汇总(齐全)

    这篇文章主要介绍了java的时间类汇总(齐全),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-09-09
  • Java ArrayAdapter用法案例详解

    Java ArrayAdapter用法案例详解

    这篇文章主要介绍了Java ArrayAdapter用法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Springboot如何加载静态图片

    Springboot如何加载静态图片

    这篇文章主要介绍了Springboot如何加载静态图片,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Springboot如何获取yml、properties参数

    Springboot如何获取yml、properties参数

    这篇文章主要介绍了Springboot如何获取yml、properties参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java中的ClassLoader类加载器使用详解

    Java中的ClassLoader类加载器使用详解

    这篇文章主要介绍了Java中的ClassLoader类加载器使用详解,ClassLoader用于将CLASS文件动态加载到JVM中去,是所有类加载器的基类,所有继承自抽象的ClassLoader的加载器,都会优先判断是否被父类加载器加载过,防止多次加载,需要的朋友可以参考下
    2023-10-10
  • Spring Boot实现热部署的实例方法

    Spring Boot实现热部署的实例方法

    在本篇文章里小编给大家整理的是关于Spring Boot实现热部署的实例方法和实例,需要的朋友们可以参考下。
    2020-02-02
  • Java获取彩色图像中的主色彩的实例代码

    Java获取彩色图像中的主色彩的实例代码

    这篇文章主要介绍了Java获取彩色图像中的主色彩的实例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Java设计模式之策略模式_动力节点Java学院整理

    Java设计模式之策略模式_动力节点Java学院整理

    策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。接下来通过本文给大家分享Java设计模式之策略模式,感兴趣的朋友一起看看吧
    2017-08-08
  • java中Locks的使用详解

    java中Locks的使用详解

    这篇文章主要介绍了java中Locks的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03

最新评论