Java - Spring中HttpServletResponse的注入原理
- 前言
- 一. HttpServletResponse的自动注入
- 1.1 用ThreadLocal保存当前的请求和返回
- 1.2 创建请求/返回的代理对象
- 1.3 请求和返回的动态加载
- 1.3.1 RequestObjectFactory的注入
- 1.3.2 RequestAttributes中请求和返回的赋值
- 二. 总结
前言
我们在每次接口调用的时候,其实都可以通过注入一个HttpServletResponse
或者HttpServletRequest
对象实例来获得当前的请求/返回。本文主要来讲解Spring
中是如何将这两个对象注入并且获取的。主要围绕着HttpServletResponse
来讲,其中也会穿插HttpServletRequest
的细节部分。
一. HttpServletResponse的自动注入
首先我们准备一个非常基础的Controller
代码:
@RestController
public class MyController {
@Autowired
private HttpServletResponse servletResponse;
@PostMapping("/hello")
public Student hello(@Validated @RequestBody Student student) {
System.out.println(servletResponse.getHeaderNames());
return student;
}
}
最终程序启动起来,当加载MyController
这个Bean
的时候,肯定是需要经过三个步骤的:(详细的可以看Spring源码系列:Bean的加载这篇文章)
- 实例化阶段。
- 属性注入。
- 初始化阶段。
那么我们在Controller
中注入的HttpServletResponse
对象实例肯定是阶段二来完成的。Spring
容器中Bean
的属性注入入口在于AbstractAutowireCapableBeanFactory.doCreateBean()
中的下面这段代码。
populateBean(beanName, mbd, instanceWrapper);
根据调用链关系:
我们最终定位到DefaultListableBeanFactory
类中。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
// 根据依赖注入源找到对应类型的Bean
Object autowiringValue = classObjectEntry.getValue();
// 为它创建一个代理对象,如果有必要的话
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
// ...
return result;
}
}
由于我们项目中注入的是HttpServletResponse
对象,因此我们找到我们想要的:
也就是说,我们注入的HttpServletResponse
它的值,就是源码中的autowiringValue
对象(当然后面可能被包装),也就是来自于这行代码:
Object autowiringValue = classObjectEntry.getValue();
同时我们注意到autowiringValue
的对象实例类型是ResponseObjectFactory
,它是WebApplicationContextUtils
下的一个静态内部类,我们来看下:
public abstract class WebApplicationContextUtils {
// 获取ServletRequest的
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
@Override
public ServletRequest getObject() {
return currentRequestAttributes().getRequest();
}
@Override
public String toString() {
return "Current HttpServletRequest";
}
}
// 获取ServletResponse的
private static class ResponseObjectFactory implements ObjectFactory<ServletResponse>, Serializable {
@Override
public ServletResponse getObject() {
ServletResponse response = currentRequestAttributes().getResponse();
if (response == null) {
throw new IllegalStateException("Current servlet response not available - " +
"consider using RequestContextFilter instead of RequestContextListener");
}
return response;
}
@Overrid
public String toString() {
return "Current HttpServletResponse";
}
}
↓↓↓↓↓↓
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;
}
}
那么根据源码,我们继续跟踪,可见最终注入的对象来源于RequestContextHolder
。无论是Request
还是Response
。都跟他有关。
RequestContextHolder.currentRequestAttributes()
1.1 用ThreadLocal保存当前的请求和返回
我们来看下RequestContextHolder
的一个大致结构:
public abstract class RequestContextHolder {
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
RequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
if (jsfPresent) {
attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
}
if (attributes == null) {
// throw
}
}
return attributes;
}
↓↓↓↓↓↓
@Nullable
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
}
我们从源码中可以看出两点:
- 我们每次请求的
ServletRequest
和ServletResponse
是放在了ThreadLocal
当中的。 -
ThreadLocal
里面指定的泛型则是RequestAttributes
。
因为RequestAttributes
是一个接口,因此我们需要看他的对应实现子类,但是呢,乍一看其子类还挺多的,那到底是哪个?
我们结合上面的代码,关于ServletResponse
的获取:
ServletResponse response = currentRequestAttributes().getResponse();
↓↓↓↓↓↓
public class ServletRequestAttributes extends AbstractRequestAttributes {
private final HttpServletRequest request;
@Nullable
private HttpServletResponse response;
@Nullable
private volatile HttpSession session;
@Nullable
public final HttpServletResponse getResponse() {
return this.response;
}
public final HttpServletRequest getRequest() {
return this.request;
}
}
那么在这一小节我们就可以得出结论:
- 和当次调用有关的请求和返回对象,都保存在
ServletRequestAttributes
这个实例当中。 ServletRequestAttributes
则又存储于ThreadLocal
,和当前线程绑定。
只不过细心的同学就会发现这么几个问题:
- 首先,我们调用10次请求,就应该对应着10个
Request
和Response
。那么Spring
也就应该做到能够实时的获取当前请求对应的实例。 那么是如何实现这样的动态加载的呢? - 我们从源码发现,还有一段代码没有讲解。也就是创建代理对象的步骤。
我们一个个来解决。
1.2 创建请求/返回的代理对象
我们回到代码中:
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
// 从ThreadLocal中拿到我们的ResponseObjectFactory 或者RequestObjectFactory
Object autowiringValue = classObjectEntry.getValue();
// 完成一个代理
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
// ...
}
}
第一个部分,我们在1.1小节有已经讲了,这里就说代理部分:
abstract class AutowireUtils {
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
// ResponseObjectFactory
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
// 此时requiredType的类型是interface javax.servlet.http.HttpServletResponse
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
// 通过JDK动态代理拿到我们的一个代理对象
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
}
else {
return factory.getObject();
}
}
return autowiringValue;
}
}
这里主要就是通过JDK
动态代理来创建出一个代理对象,同样的,既然是动态代理,我们就应该将注意力转移到代理的内容上,我们看传入的ObjectFactoryDelegatingInvocationHandler
它做了什么事情:(Java - JDK动态代理原理)
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
// 被代理对象
private final ObjectFactory<?> objectFactory;
public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
this.objectFactory = objectFactory;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (methodName.equals("hashCode")) {
// Use hashCode of proxy.
return System.identityHashCode(proxy);
}
else if (methodName.equals("toString")) {
return this.objectFactory.toString();
}
try {
return method.invoke(this.objectFactory.getObject(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
也就是说,当我们拿到HttpServletResponse / HttpServletRequest
对象实例的时候,当调用相关的方法时,实际上是代理对象通过反射来调用原始函数的。只不过代理对象对equals、hashCode、toString
这三个方法做了一点特殊处理罢了。
那么就到了最后一个问题:Spring
如何做到动态加载当前的Request/Response
?
1.3 请求和返回的动态加载
这个问题,我们可以从两个角度来思考:
- 创建
Response
和Request
的对应工厂是如何注入的? -
Spring
是如何动态改变Request
的?
1.3.1 RequestObjectFactory的注入
我们以RequestObjectFactory
为例,还记得上面的源码吗?
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
}
RequestObjectFactory
实现了ObjectFactory
接口。这里再来复习一下ObjectFactory
是干啥用的:
- 简而言之就是一个对象工厂,用来创建
Bean
对象。 - 实现了这个接口的
Bean
,会通过对应的getObject
工厂方法来获得对象。
那么RequestObjectFactory
实现这个接口,有什么目的?好巧不巧,我在Spring源码系列 - ApplicationContext容器的功能扩展这篇文章有一个章节就是讲关于容器启动阶段的后处理功能的。
我们这里拿到里面的入口函数AbstractApplicationContext.refresh
来开始分析:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// ...
try {
postProcessBeanFactory(beanFactory);
// ...
}
// ...
}
}
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
}
postProcessBeanFactory
这个函数是一个抽象类模板,具体实现则在于子类,Spring
中有这么一个AbstractApplicationContext
的子类AnnotationConfigReactiveWebServerApplicationContext
就有对应的实现:
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
// ...
}
↓↓↓↓↓
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
registerWebApplicationScopes();
}
↓↓↓↓↓
private void registerWebApplicationScopes() {
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());
// 调用WebApplicationContextUtils的注册
WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
existingScopes.restore();
}
}
到这里,仿佛一切又关联上了。
- 我们的
ServletRequest
和Response
就和WebApplicationContextUtils
息息相关。 - 而
Spring
在容器启动阶段的后处理过程中,又会调用到WebApplicationContextUtils
中的registerWebApplicationScopes
函数。
我们来看下做了什么:
// WebApplicationContextUtils
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
registerWebApplicationScopes(beanFactory, null);
}
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
@Nullable ServletContext sc) {
// ...
// 添加依赖注入的来源Bean
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
// ...
}
可见,就是注册相关的依赖,类型有:ServletRequest、ServletResponse、HttpSession、WebRequest
,这里的beanFactory
类型是DefaultListableBeanFactory
:
// DefaultListableBeanFactory
@Override
public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
Assert.notNull(dependencyType, "Dependency type must not be null");
if (autowiredValue != null) {
if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
throw new IllegalArgumentException("Value [" + autowiredValue +
"] does not implement specified dependency type [" + dependencyType.getName() + "]");
}
this.resolvableDependencies.put(dependencyType, autowiredValue);
}
}
简单点,就是将实现了ObjectFactory
接口的对象添加到依赖注入源中。
1.3.2 RequestAttributes中请求和返回的赋值
Spring
中有这么一个过滤器:RequestContextFilter
,它的代码如下:
public class RequestContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 创建一个ServletRequestAttributes类型的实例
ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
// 初始化RequestContextHolder,这里就将ServletRequestAttributes塞到ThreadLocal里面了
initContextHolders(request, attributes);
try {
// 继续执行后续逻辑
filterChain.doFilter(request, response);
}
finally {
resetContextHolders();
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
attributes.requestCompleted();
}
}
private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
}
因此我们每次请求的时候,都会被这个过滤器捕获到,然后过滤器就会把当前的HttpRequest
和Response
都塞到当前线程的ThreadLocal
中。 至此,就完成了一个闭环。
二. 总结
首先是ThreadLocal
的线程绑定问题:
- 我们每次请求,都会被拦截器
RequestContextFilter
拦截。主要就是将当前的HttpServletResponse
和HttpServletRequest
存储在ServletRequestAttributes
中。并初始化RequestContextHolder
。 - 初始化操作就是将
ServletRequestAttributes
则存储在ThreadLocal
中。这样就保证请求和当前线程的绑定。
接下来就是注入的问题:
-
Spring
容器在启动的时候,有个后处理过程会将ServletRequest、ServletResponse、HttpSession、WebRequest
注册到依赖源中。完成了RequestObjectFactory
、ResponseObjectFactory
的注入。 -
RequestObjectFactory
又实现了ObjectFactory
接口,因此获取实例的时候,调用的是对应的getObject
函数。 - 那么每次调用接口的时候,通过
getObject
函数,拿到当前的ServletRequestAttributes
。然后通过JDK
动态代理,将Request
、Response
的代理对象注入到对应的类中。