SpringMVC之分析RequestMappingHandlerAdapter(一)

本文详细解析了 SpringMVC 中 RequestMappingHandlerAdapter 的工作原理,包括其构造过程、Bean 生命周期中的作用以及如何初始化参数解析器等。

RequestMappingHandlerAdapter请求映射处理适配器,在SpringMVC中它是一个非常重要的类,对请求处理方法的调用主要是通过这个类来完成的(这段代码mv = ha.handle(processedRequest, response, mappedHandler.getHandler());)。下面我们就简单的分析一下这个类。首先我们先看一下它的UML类图结构:


画红线的部分是需要我们注意的一些类,如果你对Spring的Bean生命在周期熟悉的话,你会发现这些都是Spring的Bean生命周期相关的一些类(Spring Bean的生命周期小析(一)Spring Bean的生命周期小析(二))。我们分析RequestMappingHandlerAdapter的时候也会从这些接口的实现方法中开始。首先我们先看一下它的构造函数:

	public RequestMappingHandlerAdapter() {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

		this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(stringHttpMessageConverter);
		this.messageConverters.add(new SourceHttpMessageConverter<Source>());
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}
从上面的代码中我们可以看到这里默认添加了四种类型的Http数据转换器。其中我们需要关注的时候AllEncompassingFormHttpMessageConverter这个转换器,我们也去它的构造函数中看一下:

	public AllEncompassingFormHttpMessageConverter() {
		addPartConverter(new SourceHttpMessageConverter<Source>());
		//如果Classpath下面有javax.xml.bind.Binder类,
		//没有com.fasterxml.jackson.dataformat.xml.XmlMapper类的话
		//则添加Jaxb2RootElementHttpMessageConverter转换器
		if (jaxb2Present && !jackson2XmlPresent) {
			addPartConverter(new Jaxb2RootElementHttpMessageConverter());
		}
		//如果Classpath下有com.fasterxml.jackson.databind.ObjectMapper
		//和com.fasterxml.jackson.core.JsonGenerator的话,则添加
		//MappingJackson2HttpMessageConverter转换器
		if (jackson2Present) {
			addPartConverter(new MappingJackson2HttpMessageConverter());
		}
		//如果Classpath下面有com.google.gson.Gson类的话,则添加
		//GsonHttpMessageConverter转换器
		else if (gsonPresent) {
			addPartConverter(new GsonHttpMessageConverter());
		}
		//如果Classpath下有com.fasterxml.jackson.dataformat.xml.XmlMapper类的话,
		//则添加MappingJackson2XmlHttpMessageConverter转换器
		if (jackson2XmlPresent) {
			addPartConverter(new MappingJackson2XmlHttpMessageConverter());
		}
	}
其实在它的父类中还添加了三种类型的Convert:

	public FormHttpMessageConverter() {
		//application/x-www-form-urlencoded
		this.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
		//multipart/form-data
		this.supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
		//ByteArrayHttpMessageConverter
		this.partConverters.add(new ByteArrayHttpMessageConverter());
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);
		//StringHttpMessageConverter
		this.partConverters.add(stringHttpMessageConverter);
		//ResourceHttpMessageConverter
		this.partConverters.add(new ResourceHttpMessageConverter());

		applyDefaultCharset();
	}
按照Spring的Bean的生命周期的执行顺序,这里会先调用BeanFactoryAware#setBeanFactory方法、接着调用ApplicationContextAware#setApplicationContext方法,最后调用InitializingBean#afterPropertiesSet方法。我们先看一下重写之后的setBeanFactory方法的源码:

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (beanFactory instanceof ConfigurableBeanFactory) {
			this.beanFactory = (ConfigurableBeanFactory) beanFactory;
		}
	}
这个方法的内容很简单,就是设置一下beanFactory的属性值(其实这里的beanFactory是DefaultListableBeanFactory的实例)。
下面我们看一下setApplicationContext这个方法的源码,这个方法藏的比较深,在它的父类ApplicationObjectSupport#setApplicationContext方法中。

	public final void setApplicationContext(ApplicationContext context) throws BeansException {
		//isContextRequired()方法返回true
		if (context == null && !isContextRequired()) {
			// Reset internal context state.
			this.applicationContext = null;
			this.messageSourceAccessor = null;
		}
		else if (this.applicationContext == null) {
			// Initialize with passed-in context.
			//所传入的context如果不能被实例化,则抛出异常
			if (!requiredContextClass().isInstance(context)) {
				throw new ApplicationContextException(
						"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
			}
			this.applicationContext = context;
			//国际化
			this.messageSourceAccessor = new MessageSourceAccessor(context);
			//初始化ApplicationContext
			initApplicationContext(context);
		}
		else {
			// Ignore reinitialization if same context passed in.
			if (this.applicationContext != context) {
				throw new ApplicationContextException(
						"Cannot reinitialize with different application context: current one is [" +
						this.applicationContext + "], passed-in one is [" + context + "]");
			}
		}
	}
我们主要看一下WebApplicationObjectSupport#initApplicationContext这个方法的内容:

	@Override
	protected void initApplicationContext(ApplicationContext context) {
		//调用父类中的initApplicationContext方法
		super.initApplicationContext(context);
		if (this.servletContext == null && context instanceof WebApplicationContext) {
			this.servletContext = ((WebApplicationContext) context).getServletContext();
			if (this.servletContext != null) {
				//初始化ServletContext
				initServletContext(this.servletContext);
			}
		}
	}
这里先调用父类中的initApplicationContext方法,然后会初始ServletContext。先看一下ApplicationObjectSupport#initApplicationContext方法:

	protected void initApplicationContext(ApplicationContext context) throws BeansException {
		initApplicationContext();
	}
在我们的这个类的继承体系中,initApplicationContext();是一个空实现。同样initServletContext也是一个空实现。接下来我们要分析的一个重点InitializingBean#afterPropertiesSet的方法,它的源码内容如下,下面我们一点一点的分析这个类:

	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		initControllerAdviceCache();

		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}
我们先看initControllerAdviceCache这个方法:

	private void initControllerAdviceCache() {
		//通过上面的分析我们知道,这个方法的内容不为null
		if (getApplicationContext() == null) {
			return;
		}
		//获取所有带ControllerAdvice注解的类
		List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		//对获取到的ControllerAdvice注解的类进行排序,排序的规则是基于实现PriorityOrdered接口或者带有Order注解
		AnnotationAwareOrderComparator.sort(beans);

		List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();
		//循环获取到的ControllerAdviceBean
		for (ControllerAdviceBean bean : beans) {
			//获取所有ModelAttribute注解的方法,并且没有RequestMapping注解的方法
			Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				this.modelAttributeAdviceCache.put(bean, attrMethods);
				if (logger.isInfoEnabled()) {
					logger.info("Detected @ModelAttribute methods in " + bean);
				}
			}
			//获取所有带InitBinder注解的方法
			Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(bean, binderMethods);
				if (logger.isInfoEnabled()) {
					logger.info("Detected @InitBinder methods in " + bean);
				}
			}
			//如果实现了RequestBodyAdvice接口
			if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
				requestResponseBodyAdviceBeans.add(bean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected RequestBodyAdvice bean in " + bean);
				}
			}
			//如果实现了ResponseBodyAdvice接口
			if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
				requestResponseBodyAdviceBeans.add(bean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected ResponseBodyAdvice bean in " + bean);
				}
			}
		}
		//添加到requestResponseBodyAdvice集合中
		if (!requestResponseBodyAdviceBeans.isEmpty()) {
			this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
		}
	}
在这个方法里主要是获取了带ControllerAdvice注解的类,并从这些类中查找实现了RequestBodyAdvice或者ResponseBodyAdvice接口的类,添加到requestResponseBodyAdvice集合中,另外获取所有带ModelAttribute注解且没有RequestMapping注解的方法,放到modelAttributeAdviceCache集合中,获取所有带InitBinder注解的方法放到initBinderAdviceCache的集合中。下面我们再看这一段代码:

		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
private HandlerMethodArgumentResolverComposite argumentResolvers;
argumentResolvers这个属性是HandlerMethodArgumentResolverComposite 类型的,如果我们在配置RequestMappingHandlerAdapter的时候设置了一系列HandlerMethodArgumentResolver的实现类的话,如下所示,

	public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		if (argumentResolvers == null) {
			this.argumentResolvers = null;
		}
		else {
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite();
			this.argumentResolvers.addResolvers(argumentResolvers);
		}
	}
它会先创建HandlerMethodArgumentResolverComposite对象,然后把配置的HandlerMethodArgumentResolver的实现类添加到HandlerMethodArgumentResolverComposite的对象中。这里我们是没有手工配置RequestMappingHandlerAdapter的,所以,会先调用getDefaultArgumentResolvers方法,获取一系列默认的HandlerMethodArgumentResolver的实现类,代码如下:

	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
		// Annotation-based argument resolution
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());
		// Type-based argument resolution
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
		// Custom arguments
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}
		// Catch-all
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));
		return resolvers;
	}
SpringMVC为我们自动添加了大概26个HandlerMethodArgumentResolver的实现类。这里有一个方法需要我们注意一下:getCustomArgumentResolvers() 方法。这个方法是用来获取自定义的HandlerMethodArgumentResolver的实现类,也就是说如果我们有自己写的HandlerMethodArgumentResolver的方法的话,我们配置的属性是customArgumentResolvers,最好不要配置argumentResolvers这个属性。然后我们注意的一点是我们所添加的所以的HandlerMethodArgumentResolver的实现类,都是添加到了HandlerMethodArgumentResolverComposite这个类中,HandlerMethodArgumentResolverComposite也是HandlerMethodArgumentResolver的一个实现类。还有一点需要注意的是,这里创建了两个RequestParamMethodArgumentResolver的实例,一个传了true参数,一个传了false参数,这是用来解析不同的请求参数的,详情请看这里: SpringMVC之分析请求对应处理器方法参数的解析过程(一)

### Spring MVC 框架架构组成 Spring MVC 是基于请求驱动的 Web 框架,采用了经典的前端控制器模式进行设计。它由多个核心组件构成,这些组件协同工作以处理 HTTP 请求并返回响应。 #### 核心组件及功能 1. **前端控制器(DispatcherServlet)** DispatcherServlet 是整个框架的入口点,负责接收所有的 HTTP 请求,并协调各个组件之间的交互。它是整个流程的核心控制单元[^5]。 2. **处理器映射(HandlerMapping)** HandlerMapping 接口(如 `RequestMappingHandlerMapping`)根据 URL 找到对应的 Controller 方法。这个过程涉及对注解(例如 `@RequestMapping`)的解析和匹配。 3. **处理器适配器(HandlerAdapter)** HandlerAdapter 接口(如 `RequestMappingHandlerAdapter`)负责调用 Controller 中的方法,并处理参数绑定、数据转换以及返回值的处理。它可以支持多种类型的控制器方法,包括基于注解的方法[^5]。 4. **视图解析器(ViewResolver)** ViewResolver 接口(如 `InternalResourceViewResolver`)用于将逻辑视图名称解析为实际的视图对象(如 JSP 页面)。它决定了如何呈现最终的响应内容[^5]。 5. **异常处理器(HandlerExceptionResolver)** HandlerExceptionResolver 接口(如 `DefaultHandlerExceptionResolver`)用于捕获和处理 Controller 抛出的异常,并提供统的错误响应机制。开发者可以自定义异常处理器来实现更复杂的错误处理逻辑[^3]。 6. **区域解析器(LocaleResolver)** LocaleResolver 用于解析客户端的区域设置,以便提供国际化支持。它可以识别用户的语言偏好,并据此返回相应的本地化内容。 7. **主题解析器(ThemeResolver)** ThemeResolver 负责管理应用程序的主题样式,允许根据用户的选择或系统配置动态更改界面外观。 8. **Multipart 解析器(MultipartResolver)** MultipartResolver 用于处理文件上传请求,能够解析 multipart/form-data 类型的数据,并将其转换为易于处理的对象形式。 --- ### Spring MVC 的工作原理 Spring MVC 的工作流程主要围绕 DispatcherServlet 展开,其核心调度逻辑如下: 1. **请求进入 DispatcherServlet** 所有的 HTTP 请求首先被 DispatcherServlet 捕获。在 `doService()` 方法中,会初始化些上下文属性,然后调用 `doDispatch(request, response)` 进行进步处理[^5]。 2. **确定处理器(Handler)** DispatcherServlet 使用 HandlerMapping 查找与当前请求 URL 匹配的 Controller 方法。这过程可能涉及到 URL 参数的匹配和注解的解析。 3. **调用处理器适配器** 旦找到合适的 Controller 方法,DispatcherServlet 就会委托给相应的 HandlerAdapter 来执行该方法。HandlerAdapter 会处理方法参数的绑定、类型转换以及方法调用。 4. **处理业务逻辑** Controller 方法通常包含具体的业务逻辑,可能会访问数据库或其他服务层组件来获取或操作数据。 5. **生成模型和视图** Controller 方法执行完毕后,会返回个 ModelAndView 对象,其中包含了模型数据和视图名称。DispatcherServlet 随后使用 ViewResolver 将视图名称解析为实际的视图对象。 6. **渲染视图并返回响应** 最后,DispatcherServlet 将模型数据传递给视图进行渲染,并通过 HttpServletResponse 返回最终的 HTML 或其他格式的响应。 在整个过程中,如果发生异常,DispatcherServlet 会调用 HandlerExceptionResolver 来处理这些异常,并返回适当的错误页面或 JSON 响应。 --- ### 示例代码:简单的 Spring MVC 控制器 以下是个简单的 Spring MVC 控制器示例,展示了如何定义个处理 GET 请求的 Controller 方法。 ```java import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HelloController { @GetMapping("/hello") @ResponseBody public String sayHello() { return "Hello, World!"; } } ``` 在这个例子中: - `@Controller` 注解标记了该类为个 Spring MVC 控制器。 - `@GetMapping` 注解指定了该方法处理 `/hello` 路径上的 GET 请求。 - `@ResponseBody` 注解表示该方法直接返回字符串作为响应体,而不是视图名称。 --- ### 总结 Spring MVC 提供了个灵活且强大的 Web 开发框架,通过分离关注点(即 Model、View 和 Controller),使得开发人员可以更容易地构建可维护和可扩展的应用程序。它的核心组件相互协作,确保每个请求都能得到正确处理,并生成预期的响应。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值