• 在Spring的bean中注入HttpServletRequest解密


    我们可以在Spring的bean中轻松的注入HttpServletRequest,使用@Autowired HttpServletRequest request;就可以了。

    但是,为什么我们可以直接这样用呢?

    原因肯定是Spring在容器初始化的时候就将HttpServletRequest注册到了容器中。

    那么我们就查原码,发现在WebApplicationContextUtils.registerWebApplicationScopes(ConfigurableListableBeanFactory, ServletContext)中实现了这个功能。

    这个方法是在AbstractApplicationContext.refresh()中进行调用的,也就是在Spring容器初始化的时候,将web相关的对象注册到了容器中。

    具体可以看下面的原码图片:

    调用处:

    问题: 

    注入 HttpServletRequest 为什么每次都能拿到最新的?bean 的依耐关系不是容器启动后就确定了吗?

    解析:
    spring 注册的 HttpServletRequest 类型的 bean 是一个 RequestObjectFactory,如果进行依赖注入时是通过 RequestObjectFactory.getObject() 获取 request 对象的话,那么依赖关系在这个时候就确定了,这样肯定不能达到每次 http 请求都拿到一个新的 HttpServletRequest 对象的目的。
    写一个 controller ,注入 HttpServletRequest,打断点查看注入的对象是一个 JDK 代理对象, 对应的 InvocationHandler 是 AutowireUtils$ObjectFactoryDelegatingInvocationHandler。通过查找 ObjectFactoryDelegatingInvocationHandler 使用的地方,发现是在 AutowireUtils.resolveAutowiringValue() 时创建的代理对象。再在这个方法上打个断点,重新启动容器,就可以发现,HttpServletRequest 是在 XxController 这个 bean 注入依赖属性时 (populateBean() )调用的 AutowireUtils.resolveAutowiringValue() 创建的代理。
    这样就说的通了:因为 spring 在注入 HttpServletRequest 时,发现如果注入的是 一个 ObjectFactory 类型的对象时,就会将注入的 bean 替换成一个 JDK 动态代理对象,代理对象在执行 HttpServletRequest 对象里的方法时,就会通过 RequestObjectFactory.getObject() 获取一个 新的 request 对象来执行。
     

    RequestObjectFactory

     1 private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
     2     @Override
     3     public ServletRequest getObject() {
     4         return currentRequestAttributes().getRequest();
     5     }
     6 
     7     @Override
     8     public String toString() {
     9         return "Current HttpServletRequest";
    10     }
    11 }
    View Code

    ObjectFactoryDelegatingInvocationHandler

     1 private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
     2 
     3     private final ObjectFactory<?> objectFactory;
     4 
     5     public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
     6         this.objectFactory = objectFactory;
     7     }
     8 
     9     @Override
    10     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    11         String methodName = method.getName();
    12         if (methodName.equals("equals")) {
    13             // Only consider equal when proxies are identical.
    14             return (proxy == args[0]);
    15         }
    16         else if (methodName.equals("hashCode")) {
    17             // Use hashCode of proxy.
    18             return System.identityHashCode(proxy);
    19         }
    20         else if (methodName.equals("toString")) {
    21             return this.objectFactory.toString();
    22         }
    23         try {
    24             return method.invoke(this.objectFactory.getObject(), args);
    25         }
    26         catch (InvocationTargetException ex) {
    27             throw ex.getTargetException();
    28         }
    29     }
    30 }
    View Code

    附:

    1. Spring能实现在多线程环境下,将各个线程的request进行隔离,且准确无误的进行注入,奥秘就是ThreadLocal

    2. Spring中还可以直接注入ApplicationContext

  • 相关阅读:
    餐巾计划问题 zwk费用流解法
    Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵
    smarty之缓存机制
    mysql中 where in 用法详解
    sql语句中left join、inner join中的on与where的区别
    PHP表单数组的具体使用方法介绍
    document.body.scrollTop值为0的解决方法[转]
    left join on and和left join on where条件的困惑[转]
    Uedit32_17.00 修改某一语言背景色-修改后续名后语法着色及某语言的大括号{}对齐
    CSS 针对谷歌浏览器(Chrome) safari的webkit核心浏览器CSS hack
  • 原文地址:https://www.cnblogs.com/kevin-yuan/p/5336124.html
Copyright © 2020-2023  润新知