• SpringMVC Controller中注入Request成员域和在方法中定义中HttpServletRequest有啥区别


    先说结论,在Controller中注入Request是线程安全的。

    以下是解释:

    我们先来看看这两者有什么不同

    controller注入成员变量request

    可以看到注入的是一个代理对象

    写在方法参数上

    可以看到是一个tomcat原生的RequestFacade对象

    那接下来我们看看controller注入成员变量request是怎么实现的?

    可以看到,我们找到

    org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler

    @SuppressWarnings("serial")
    	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();
    			}
    		}
    	}
    

      

    可以看到 当代理对象被调用的时候,会先调用这个handler里面的

    ObjectFactory.getObject()方法获取相关对象 在执行该对象的相关方法

    AbstractApplicationContext抽象类是ApplicationContext的抽象实现类,里面的refresh()方法定义了Spring容器在加载配置文件后的各项处理工作。

    其中定义了一个模板方法

    postProcessBeanFactory(beanFactory);

    AbstractRefreshableWebApplicationContext覆盖了这个方法

    	@Override
    	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
    		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    		beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
    		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
    		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
    	}
    

      

    其中在这个方法里面

    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);

    这里设置了一些特殊的beanscope,比如request,session,并设置了一些比较特殊的注入值

    public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
    		beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    		beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
    		beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
    		if (sc != null) {
    			ServletContextScope appScope = new ServletContextScope(sc);
    			beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
    			// Register as ServletContext attribute, for ContextCleanupListener to detect it.
    			sc.setAttribute(ServletContextScope.class.getName(), appScope);
    		}
    //这里对这些特殊值注入相关的ObjectFactory
    		beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
    		beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
    		beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
    		beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
    		if (jsfPresent) {
    			FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
    		}
    	}
    

      

    而上面的代码。当我们调用Controller成员变量的request的时候,都会通过

    RequestObjectFactorygetobject获取到真正的对象 并执行他的方法

    接着去看看 RequestObjectFactory

    @SuppressWarnings("serial")
    	private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
    		@Override
    		public ServletRequest getObject() {
    			return currentRequestAttributes().getRequest();
    		}
    		@Override
    		public String toString() {
    			return "Current HttpServletRequest";
    		}
    	}
    

      我们点进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;
    	}
    

        发现是从RequestContextHolder中获取的

    继续点击RequestContextHolder

    最后发现其实request是从threadLocal中取...

     

    那问题又来了 request 是什么时候放到threadlocal 里面的?

    是在SpringmvcdispatcherServlet的父类FrameworkServlet里操作的.

     

    @Override
    	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		processRequest(request, response);
    	}
    
    	/**
    	 * Delegate POST requests to {@link #processRequest}.
    	 * @see #doService
    	 */
    	@Override
    	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		processRequest(request, response);
    	}
    

     

      不管你是doGet还是doPost还是doXXX方法都是委托processRequest方法去做的.

    我们再看看 processRequest() 这个方法

    其中调用了initContextHolders方法将request放到ThreadLocal里面去的

     

     

    总结;

    1这样子使用是可以的,不会有线程安全问题,

    2基于此我们封装一个CommonController 把一些获取数据的方法可以封装在里面。比如通过token获取用户信息等

     

     

  • 相关阅读:
    linux内核剖析(六)Linux系统调用详解(实现机制分析)
    Linux内核剖析(五)Linux内核的构建过程
    Linux内核剖析(四)为arm内核构建源码树
    Linux内核剖析(三)构建源码树
    Linux内核剖析(二)Linux内核绪论
    kubectl更新镜像和回滚命令
    Linux登录shell和非登录(交互式shell)环境变量配置
    Elasticsearch7.6学习笔记1 Getting start with Elasticsearch
    docker安装Elasticsearch7.6集群并设置密码
    Jenkinsfile里定义对象和函数,获取git提交人, 发送钉钉通知
  • 原文地址:https://www.cnblogs.com/javabigdata/p/7523017.html
Copyright © 2020-2023  润新知