zoukankan      html  css  js  c++  java
  • 在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

  • 相关阅读:
    VirtualBox Win7 虚拟机 共享文件夹设置
    图像的 SNR 和 PSNR 的计算
    swprintf %s %ws %S 的区别
    解决 Virtual Box 启动 Cannot load R0 module supLoadModule returned VERR_LDR_MISMATCH_NATIVE Failed to register ourselves as a PCI Bus (VERR_MODULE_NOT_FOUND)
    VS2015/2013/2012 IIS Express Debug Classic ASP
    Beginning Android 4 中 Demo Basic/Switch 的问题.
    【虚拟机】解决网络适配器没有 VirtualBox Host-Only Ethernet Adapter 问题
    Docker安装Tomcat镜像并部署web项目
    Docker——入门实战
    Centos7上安装docker
  • 原文地址:https://www.cnblogs.com/kevin-yuan/p/5336124.html
Copyright © 2011-2022 走看看