HttpServletRequest
是由如tomcat这种Servlet容器创建的。
Tomcat 创建的 Request 和 Response 的类结构图。
至于这个HttpServletRequest
是怎么传到你对应的controller方法内的,实际上就是SpringMVC里面的DispatchServlet
起的作用。这里先不展开,下面再讨论,先看一张图。
得到HttpServletRequest的三种方式
方式一:Controller方法参数
在Controller
的方法参数上写上HttpServletRequest
,这样每次请求过来得到就是对应的HttpServletRequest
。
@GetMapping("/test")
public void test(HttpServletRequest request) {}
方式二:从RequestContextHolder上下文获取
// 从请求上下文里获取Request对象
ServletRequestAttributes requestAttributes = ServletRequestAttributes.class.
cast(RequestContextHolder.getRequestAttributes());
HttpServletRequest contextRequest = requestAttributes.getRequest();
方式三:依赖注入@Autowired
@Autowired
HttpServletRequest req;
@GetMapping("/test")
public void test(HttpServletRequest request) {}
注入的request何处来?
image.png发现是注入其实是往WebApplicationContextUtils通过RequestObjectFactory拿值,跟踪
image返回的是RequestContextHolder里的值. 追踪RequestContextHolder
image.png每次返回的其实是, RequestAttributes的实现类ServletWebRequest(ServletRequestAttributes)里的request. 因为RequestAttributes是属于threadLocal的,所以注入的request也是线程安全的了
2, spring何时设置的request对象?
HttpServlet实现类 FrameworkServlet-> service()->processRequst()
image每次请求都会往里面设置最新的request, 设值
================================================================
我们可以在Spring的bean中轻松的注入HttpServletRequest,使用@Autowired HttpServletRequest request;就可以了。
但是,为什么我们可以直接这样用呢?
原因肯定是Spring在容器初始化的时候就将HttpServletRequest注册到了容器中。
那么我们就查原码,发现在WebApplicationContextUtils.registerWebApplicationScopes(ConfigurableListableBeanFactory, ServletContext)中实现了这个功能。
这个方法是在AbstractApplicationContext.refresh()中进行调用的,也就是在Spring容器初始化的时候,将web相关的对象注册到了容器中。
具体可以看下面的原码图片:
image image image image附:
1. Spring能实现在多线程环境下,将各个线程的request进行隔离,且准确无误的进行注入,奥秘就是ThreadLocal
image2. Spring中还可以直接注入ApplicationContext
网友评论