controller中如何获取request&response:
法1
@GetMapping("getRequest")
public void getRequest(){
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();
System.out.println(request.getClass());
System.out.println(response.getClass());
System.out.println("get!");
}
// class org.apache.catalina.connector.RequestFacade
// class org.apache.catalina.connector.ResponseFacade
// get!
法2
@GetMapping("getRequest")
public void getRequest(HttpServletRequest request,HttpServletResponse response){
System.out.println(request.getClass());
System.out.println(response.getClass());
System.out.println("get!");
}
// class org.apache.catalina.connector.RequestFacade
// class org.apache.catalina.connector.ResponseFacade
// get!
法3
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@GetMapping("getRequest")
public void getRequest(){
System.out.println(request.getClass());
System.out.println(response.getClass());
System.out.println("get!");
}
// class com.sun.proxy.$Proxy90
// class com.sun.proxy.$Proxy106
// get!
前两中方式获取到的org.apache.catalina.connector.RequestFacade其实是Tomcat使用Facde模式对Request对象进行了包装,由于Request本身提供的接口非常之多,而系统中只需要使用其部分功能,所以Request的具体工作最后delegate到底层的coyote.Request去做,中规中矩。
那关于第三种方法,com.sun.proxy.$Proxy90我们可以发现这是一个JDK动态代理对象。这也引申出一个问题:Controller是单例的,而request是一个变化的共享变量,对于每个请求来说都是不一样的,通过@Autowired注入会不会有线程安全问题?
答案是:不会有线程安全问题,注入的HttpServletReuqest的实现类为JDK动态代理生成的一个代理类,从Request中获取值的时候是从ThreadLocal中得到的对象中的值。
流程
我们知道,@Autowired注入的动态代理对象的InvocationHandler处理器均为AutowireUtils.ObjectFactoryDelegatingInvocationHandler,也就是说在我们调用request的方法时会转而调用到ObjectFactoryDelegatingInvocationHandler的invoke方法:
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
private final ObjectFactory<?> objectFactory;
public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
this.objectFactory = objectFactory;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("equals")) {
return proxy == args[0];
} else if (methodName.equals("hashCode")) {
return System.identityHashCode(proxy);
} else if (methodName.equals("toString")) {
return this.objectFactory.toString();
} else {
try {
// 比如调用request.getParameter("helo");会流转到这里
return method.invoke(this.objectFactory.getObject(), args);
} catch (InvocationTargetException var6) {
throw var6.getTargetException();
}
}
}
}
可以看到,只要不是equals,hashCode,toString这几个有限的方法,其余都是通过objectFactory.getObject()来调用的,而request使用的是RequestObjectFactory,
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
@Override
public ServletRequest getObject() {
return currentRequestAttributes().getRequest();
}
@Override
public String toString() {
return "Current HttpServletRequest";
}
}
可以看到,最终返回是currentRequestAttributes().getRequest(),我们接着进入currentRequestAttributes()方法:
private static ServletRequestAttributes currentRequestAttributes() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return (ServletRequestAttributes) requestAttr;
}
可以看出,这个方法中的代码,与上面的法1完全相同,也就是((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest(),那么RequestContextHolder.currentRequestAttributes()的值哪儿来的,我们继续点进去:
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
可以看出,通过requestAttributesHolder或者inheritableRequestAttributesHolder的get()方法,来获取的RequestAttributes对象。
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
而这两个holder,从命名我们就可以猜出来这两是ThreadLocal。所以我们每次都是从ThreadLocal中取得值,那当然是线程安全的。
现在我们有了一个概念:RequestContextHolder它代表着请求上下文,内部使用ThreadLocal来维护着,用于在线程间传递RequestAttributes数据。那么,啥时候放进去的?
我们找到了set()方法的相关调用方:RequestContextFilter、RequestContextListener以及FrameworkServlet。其中RequestContextFilter和RequestContextListener都是特殊场景才会使用的,这里不去关注。在FrameworkServlet中initContextHolders()以及resetContextHolders()方法都会维护上下文,也就是调用RequestContextHolder.setRequestAttributes()方法。
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
private void resetContextHolders(HttpServletRequest request,
@Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) {
RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
}
至此,我们已经知道只要请求交给了FrameworkServlet处理,那么请求上下文里就必然有Request/Response等实例,并且是和每个请求线程绑定的。
对了,我们熟知的DispatcherServlet是FrameworkServlet子类。
参考:
https://blog.csdn.net/f641385712/article/details/104579949
https://blog.csdn.net/zknxx/article/details/77917290
https://blog.csdn.net/webzhuce/article/details/72805896