SpringMVC之自动注入Request对象

简介: 前几天看了领导写的一段代码,在Controller中注入了HttpServletRequest,形式如下所示:@RestControllerpublic class AutowiredRequestController { @Aut...

前几天看了领导写的一段代码,在Controller中注入了HttpServletRequest,形式如下所示:

@RestController
public class AutowiredRequestController {

    @Autowired
    private HttpServletRequest request;
}
当时看到了这一段代码,首先想到的是AutowiredRequestController是一个singleton的bean,HttpServletRequest是一个变化的共享变量,每个请求对象都是不一样的,这样写不会有线程安全问题吗?带着疑问去翻了翻SpringMVC的源码,结论是:不会有线程安全问题!!!不会有线程安全问题!!!!不会有线程安全问题!!!下面我们来分析一下:

在前面的文章中我们简单的分析过SpringMVC的上下文初始化过程(SpringMVC之浅析上下文初始化(一) SpringMVC之浅析上下文初始化(二)),SpringMVC环境中的父上下文时:XmlWebApplicationContext。下面我们先看一下XmlWebApplicationContext的UML类图(去掉了一些暂时无关的):


之前也说过会调用AbstractApplicationContext中的refresh方法进行Bean的组装初始化的过程,在refresh()这个方法中会调用:postProcessBeanFactory()这个方法,设置一些需要预先处理的Bean。而XmlWebApplicationContext继承了AbstractRefreshableWebApplicationContext,AbstractRefreshableWebApplicationContext中重写了postProcessBeanFactory这个方法,我们去AbstractRefreshableWebApplicationContext这个类中看一下这个方法的内容:

	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		//注册Bean的生命周期的相关的类(实现了BeanPostProcessor接口)
		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);这个方法是我们需要重点分析的类,我进去看一下:

	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);
		}

		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);
		}
	}
在这个类中注册了Web开发相关的三个Scope:RequestScope、SessionScope和全局的SessionScope以及一个ServletContextScope。接下来了注册了几个实现了ObjectFactory的Bean。RequestObjectFactory、RequestObjectFactory、SessionObjectFactory、WebRequestObjectFactory。这几个类是在Controller中注入Request和Response的关键。我们先记住 RequestObjectFactory这个类。这几个类被放到了resolvableDependencies中。resolvableDependencies这个Map中存放的是在Autowire时所要使用到的bean的类型和对应的Object。

下面我们去看一下在Controller中注入Request的一个过程。首先看一下相应的一些调用链:


这个调用链比较长,调用链的执行过程也很复杂,这里先不过多展开的,我们直接进入到关键代码中。先进入到DefaultListableBeanFactory的findAutowireCandidates这个方法中,看一些关键代码:

	protected Map<String, Object> findAutowireCandidates(
			String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
		for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
			if (autowiringType.isAssignableFrom(requiredType)) {
				Object autowiringValue = this.resolvableDependencies.get(autowiringType);
				autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
				if (requiredType.isInstance(autowiringValue)) {
					result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
					break;
				}
			}
		}
		//省略其他代码
		...........
	}
在上面的代码中我们看到了resolvableDependencies这个属性。当注入HttpServletRequest的时候,requiredType值是HttpServletRequest.class,我们还记得在resolvableDependencies中放入了ServletRequest.class这个key。所以 if (autowiringType.isAssignableFrom(requiredType)) 这个判断会返回true,接着会取得RequestObjectFactory这个对象。接着会调用AutowireUtils.resolveAutowiringValue这个方法进一步的解析Autowire的值。我们进到这个方法中进行看一下:

	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;
	}
在这个方法中会先判断上一步取得的autowireValue是不是可以实例化为ObjectFactory 对象。上一步取得的autowireValue是RequestObjectFactory。我们看一下RequestObjectFactory这个类的内容:

	private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}

		@Override
		public String toString() {
			return "Current HttpServletRequest";
		}
	}
很明显的实现了ObjectFactory这个接口。接着RequestObjectFactory 是不能实例化为HttpServletRequest对象的。所以会进入到循环体中。接着进一步的判断上一步取得的autowireValue的值能不能被序列化,以及requiredType是不是可以接口,从上面的分析可以看出这个条件也是成立的。所以最终生成的autowireValue的值是一个 JDK动态代理生成的对象。InvocationHandler的实现类为:ObjectFactoryDelegatingInvocationHandler。所以我们在Controller层中所注入的HttpServletRequest其实是一个JDK动态代理生成的对象。这个是很关键的一个 !!!当我们在Controller中调用的时候:

    @RequestMapping("testAutowiredRequest")
    public String testAutowiredRequest() {
        request.getParameter("userNmae");
        return "success";
    }
会进入到ObjectFactoryDelegatingInvocationHandler的invoke方法中。我们去看一下:

		@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();
			}
		}
这里对equals方法、hashcode方法、toString方法进行了特殊处理。其他的方法则是直接执行method.invoke的方法。第一个参数为所调用的对象。是通过调用this.objectFactory.getObject()来获取的。objectFactory,通过我们上面的分析我们知道它是RequestObjectFactory这个对象。所以我们去RequestObjectFactory这个类中看一下objectFactory这个方法:

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}
在上面的代码中调用了currentRequestAttributes().getRequest()。我们接着去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;
	}
看到这里时,你是不是会有一种恍然大悟的感觉呢?((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();这种方式不也是我们经常在代码中获取HttpServletRequest对象的方式吗?我在进一步的分析一下。看一下RequestContextHolder.currentRequestAttributes()这个方法:

	public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
		RequestAttributes attributes = getRequestAttributes();
		if (attributes == null) {
			if (jsfPresent) {
				attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
			}
			if (attributes == null) {
				throw new IllegalStateException("");
			}
		}
		return attributes;
	}
通过getRequestAttributes()这个方法来获取RequestAttributes对象。我们进入到getRequestAttributes()这个方法中继续看一下:

	public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		}
		return attributes;
	}
发现是通过requestAttributesHolder或者inheritableRequestAttributesHolder的get()方法,来获取的RequestAttributes对象。那么requestAttributesHolder和inheritableRequestAttributesHolder是什么呢?

private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<RequestAttributes>("Request attributes");

private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<RequestAttributes>("Request context");
两个 ThreadLocal的对象!!!到这里总算是一切都真相大白了!!!每次都是从ThreadLocal对象中取的值,那还能不是线程安全的吗?这里在多说一句,什么时候往这两个ThreadLocal中放入值的呢?请接着往下看:org.springframework.web.servlet.FrameworkServlet#processRequest这个方法中有这样的几行代码:

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);//创建ServletRequestAttributes 
		initContextHolders(request, localeContext, requestAttributes);//往ThreadLocal中放入值
initContextHolders这个方法的代码如下:

	private void initContextHolders(
			HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		//把RequestAttributes放入到ThreadLocal中
		if (requestAttributes != null) {
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
	}

RequestContextHolder.setRequestAttributes这个方法的内容如下:

	public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
		if (attributes == null) {
			resetRequestAttributes();
		}
		else {
			if (inheritable) {
				inheritableRequestAttributesHolder.set(attributes);
				requestAttributesHolder.remove();
			}
			else {
				requestAttributesHolder.set(attributes);
				inheritableRequestAttributesHolder.remove();
			}
		}
	}
不需要再多说了。

这里总结一下:Controller层中所注入的HttpServletReuqest的实现类为JDK动态代理生成的一个代理类,从Request中获取值的时候是从ThreadLocal中得到的对象中的值。

相关文章
|
6月前
|
应用服务中间件 Apache
springmvc中报错Request processing failed;
springmvc中报错Request processing failed;
SpringMVC入门到实战------5、域对象共享数据 Request、Session、Application、Model、ModelAndView、Map、ModelMap的详细使用及代码实例
这篇文章详细解释了在IntelliJ IDEA中如何使用Mute Breakpoints功能来快速跳过程序中的后续断点,并展示了如何一键清空所有设置的断点。
SpringMVC入门到实战------5、域对象共享数据 Request、Session、Application、Model、ModelAndView、Map、ModelMap的详细使用及代码实例
|
6月前
|
前端开发 Java Spring
Spring MVC 出现Request method ‘GET‘ not supported解决办法
Spring MVC 出现Request method ‘GET‘ not supported解决办法
|
6月前
SpringMVC 域对象共享数据
SpringMVC 域对象共享数据
30 0
|
6月前
【SpringMVC】SpringMVC方式,向作用域对象共享数据(ModelAndView、Model、map、ModelMap)
【SpringMVC】SpringMVC方式,向作用域对象共享数据(ModelAndView、Model、map、ModelMap)
66 1
|
6月前
SpringMVC之获取请求参数和域对象共享数据
【1月更文挑战第18天】 一、SpringMVC获取请求参数 1、通过ServletAPI获取 2、通过控制器方法的形参获取请求参数 3、@RequestParam 4、@RequestHeader 5、@CookieValue 6、通过POJO获取请求参数 7、解决获取请求参数的乱码问题 二、域对象共享数据 1、使用ServletAPI向request域对象共享数据 2、使用ModelAndView向request域对象共享数据 3、使用Model向request域对象共享数据 4、使用map向request域对象共享数据 5、使用ModelMap向request域对象共享数据
106 0
|
6月前
|
前端开发 Java 应用服务中间件
SpringMVC源码分析之策略对象初始化
SpringMVC源码分析之策略对象初始化
62 0
|
6月前
|
Java Spring
SpringMVC控制层private方法中出现注入的service对象空指针异常
一、现象 SpringMVC中controller里的private接口中注入的service层的bean为null,而同一个controller中访问修饰符为public和protected的方法不会出现这样的问题。 controller中的方法被AOP进行了代理,普通Controller如果没有AOP,private方法中bean也是正常的。
|
Java Spring
springMVC中获取request 对象
springMVC中获取request 对象
|
存储 JavaScript 前端开发
SpringMVC 域对象共享数据
SpringMVC 域对象共享数据