java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot注入成员变量HttpServletRequest原理

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

作者: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时,可以把这种设计放在代码中,实现优雅和保底逻辑。

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

您可能感兴趣的文章:
阅读全文