曹耘豪的博客

Spring之HttpServletRequest注入和ObjectFactory原理

  1. 为什么可以直接注入HttpServletRequest
    1. 猜测
    2. 验证

为什么可以直接注入HttpServletRequest

如果我们要使用HttpServletRequest,可以通过Controller的参数,也可以如下,直接在Controller(也可以是Service)注入HttpServletRequest,那这是怎么实现的呢?

XxxController.java
1
2
@Autowired
private HttpServletRequest httpServletRequest;

注入函数在 WebApplicationContextUtils#registerWebApplicationScopes

1
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());

RequestObjectFactory是一个ObjectFactory,其中getObject方法返回ServletRequest接口,方法内部,每次取从ThreadLocal里取RequestAttributesWebApplicationContextUtils#currentRequestAttributes

set的时机,是在请求时,通过Filter进行set,RequestContextFilter#initContextHolders

猜测

所以我们猜测,每次在访问注入的对象的方法时,都通过ObjectFactory.getObject获取到真实操作的对象,然后再调用对应的方法

验证

注册时调用的是registerResolvableDependency,看源码可知添加到resolvableDependencies,解析注入依赖时,会调用AutowireUtils#resolveAutowiringValue,如下,可以看到,如果注册的是ObjectFactorySerializable且返回类型是接口,则注入JDK动态代理对象getObject将会被多次调用(每调用一次方法都会调用一次),否则直接注入getObject的结果,此时getObject只会执行一次。

AutowireUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
}
else {
return factory.getObject();
}
}
return autowiringValue;
}

JDK动态代理的InvocationHandler如下:

ObjectFactoryDelegatingInvocationHandler.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "equals":
// Only consider equal when proxies are identical.
return (proxy == args[0]);
case "hashCode":
// Use hashCode of proxy.
return System.identityHashCode(proxy);
case "toString":
return this.objectFactory.toString();
}
try {
return method.invoke(this.objectFactory.getObject(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}

得到验证

   /