Controller成员变量注入HttpServletRequest安全吗?

问题:

在SpringMVC的应用中,我们使用HttpServletRequest时都是将其直接放到Controller方法中的入参,但是这样十分冗余, 如果我们可以使用@Autowired注解配合Spring的注入机制在Controller中让HttpServletRequest作为成员变量注入,那需要使用session的方法入参中就少了一个参数,但是这样做真的安全吗?

Spring默认的注入的bean的作用域是单例的,使用这种方式会不会导致HttpServletRequest也是单例的从而出现线程不安全的情况?

接下来看看例子,例子源代码:

@Controller
@RequestMapping("/request")
public class RequestController {

    @Autowired
    HttpServletRequest request;

    @RequestMapping("/methodParam")
    @ResponseBody
    public String byMethodParam(HttpServletRequest httpServletRequest) {
        String id = httpServletRequest.getParameter("id");
        return id;
    }

    @ResponseBody
    @RequestMapping("/memberVariable")
    public String byMemberVariable() {
        String id = request.getParameter("id");
        return id;
    }
}

1. 方法参数使用流程分析:

我们先在byMethodParam(HttpServletRequest httpServletRequest)方法中打上断点,看看方法入参的HttpServletRequest 是什么对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Uj2iEag-1599138406075)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/854c6f9c-7d7b-4bf3-bd09-8727469bb991/Untitled.png)]

我们可以看到,这里的HttpServletRequest是个SecurityContextHolderAwareRequestWrapper对象,我们再进入SecurityContextHolderAwareRequestWrapper对象看看它的继承关系:

SecurityContextHolderAwareRequestWrapper继承了HttpServletRequest,所以实际上它是个Servlet中最原生的HttpServletRequest对象。

2. 成员变量注入流程分析:

接下来,我们再进入byMemberVariable方法中打断点。

在这里插入图片描述

可以看到,作为成员变量注入的HttpServletRequest对象实际的类型与上面的不一样了。它实际上是一个AutowireUtils类的静态内部类ObjectFactoryDelegatingInvocationHandler
在这里插入图片描述

这个静态内部类ObjectFactoryDelegatingInvocationHandler实际上继承了InvocationHandler, 实际上它是一个代理,在代理实现的invoke()方法中,我们重点看method.invoke(this.objectFactory.getObject(), args);这里代理方法使用的对象是this.objectFactory.getObject()获取的, 断点走下来看看objectFactory的对象类型:

在这里插入图片描述
它实际上是WebApplicationContextUtils类中定义的静态内部类RequestObjectFactory,我们再看看这个RequestObjectFactory 类中定义的方法:

在这里插入图片描述

我们再进入currentRequestAttributes() 方法:

在这里插入图片描述

再进入RequestContextHolder.currentRequestAttributes() 方法:

在这里插入图片描述

再进入getRequestAttributes()方法:

在这里插入图片描述

再看看requestAttributesHolder 是个什么东西:

[

看到这里就真相大白了,这里实际上使用了一个ThreadLocal 存着RequestAttributes , 而HttpServletRequest 是在RequestAttributes 对象中获取的,这里用了ThreadLocal,所以每个请求都对应了一条线程自己的RequestAttributes ,所以HttpServletRequest 也是每条请求线程中私有的,所以它是线程安全的。

3. 引申问题:

那SpringMVC是什么时候将HttpServletRequest 对象设置到ThreadLocal中的呢?

我们先看DispatcherServlet 类,这里没有发现相关方法,再看它的父类FrameworkServlet ,找到FrameworkServlet中的doService()、doGet()、doPost()等几个基本的Http处理方法,里面都执行了processRequest(HttpServletRequest request, HttpServletResponse response)方法:

在这里插入图片描述

再进入processRequest(HttpServletRequest request, HttpServletResponse response)一探究竟:

在这里插入图片描述

这里从RequestContextHolder中获取了RequestAttributes 对象,再看看initContextHolders()方法:

在这里插入图片描述

原来就是在这个地方设置了RequestAttributes

简单来说,就是对每个请求进行处理时,在FrameworkServletprocessRequest(HttpServletRequest request, HttpServletResponse response)设置了RequestAttributesRequestContextHolder 类的ThreadLocal<RequestAttributes>变量中。

4. 总结:

使用方法参数传递的HttpServletRequest ,框架没有做封装,直接使用的是原生的HttpServletRequest 对象。

使用方法成员变量和@Autowired注解注入的HttpServletRequest ,实际上SpringMVC做了一层代理,最终获取的是RequestContextHolder 中的类型为ThreadLocal<RequestAttributes>中存放的

RequestAttributes中的HttpServletRequest 对象,因为使用了ThreadLocal,所以保证了每条线程

HttpServletRequest 对象的隔离,从而实现线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值