【Spring】抽丝剥茧SpringMVC-RequestMappingHandlerAdapter

源码基于SpringMVC 5.2.7版本

RequestMappingHandlerAdapter是SpringMVC核心组件之一,其负责请求的参数解析、调用请求具体处理方法、返回值处理。尤其参数解析、返回值处理这两项工作相当复杂。RequestMappingHandlerAdapter又分别交给参数解析器HandlerMethodArgumentResolver以及返回处理器HandlerMethodReturnValueHandler完成。RequestMappingHandlerAdapter执行完毕后将返回ModelAndView对象。ModelAndView包含了数据(model)和视图(view)。

    request经过HandlerMapping找到了对应的Handler。但是要执行Handler,还需要从request中提取handler需要的参数,Handler执行后返回值也需要经过处理才能写入response返回给客户端。这些操作都是由MVC另一个核心组件-HandlerAdapter完成。

    HandlerAdapter核心事情主要有3件:参数预处理、调用Handler、返回值处理。

  • 参数预处理是从request的body、query、header等解析并转换为Handler对应的参数,这部分工作有一系列的参数解析器完成
  • 调用Handler就是通过反射执行handler的方法体
  • 返回值处理是对Handler的返回值进行处理,这部分工作由一组返回值处理器完成

HandlerAdapter初始化

首先了解一下DispatcherServlet如何确定请求对应的HandlerAdapter?DispatcherServlet本身的初始化的时候根据配置获取一组HandlerAdapter,依次遍历HandlerAdapter是否支持该请求对应的Handler。SpringMVC主要支持的HandlerAdapter类型有

 

HandlerAdapter类型

 

支持的Handler

 

备注

 

RequestMappingHandlerAdapter

 

org.springframework.web.method.HandlerMethod

 

RequestMappingHandlerMapping返回的Handler就是HandlerMethod

 

HandlerFunctionAdapter

 

org.springframework.web.servlet.function.HandlerFunction

 

Spring5.2开始支持的一种Handler

 

HttpRequestHandlerAdapter

 

org.springframework.web.HttpRequestHandler

 

 

 

SimpleControllerHandlerAdapter

 

org.springframework.web.servlet.mvc.Controller

 

 

 

SimpleServletHandlerAdapter

 

javax.servlet.Servlet

 

 

DispatcherServlet初始化HandlerAdapter逻辑与其初始化HandlerMapping逻辑类似,

  1. 如果"detectAllHandlerAdapters"打开,则从IOC容器中获取所有类型为HandlerAdapter的实例;否则进入2
  2. 从IOC容器中获取name为"handlerAdapter"的实例;
  3. 如果步骤1、2之后已经有HandlerAdapter则装配过程结束,否则进入4;
  4. 通过DispatcherServlet默认装配策略中创建HandlerAdapter实例,并装配给DispatcherServlet,装配过程结束。

默认情况“detectAllHandlerAdapters”是打开的,也就是说默认情况是从IOC容器中找到所有HandlerAdapter的Bean。DispatcherServlet默认装配策略在《抽丝剥茧MVC之RequestMappingHandlerMapping》中有介绍,这里就不赘述。

RequestMappingHandlerAdapter

在SpringMVC支持的几种HandlerAdapter中,最常用的就是RequestMappingHandlerAdapter。这种HandlerAdapter是配合@RequestMapping使用的。

RequestMappingHandlerAdapter主要成员

  • argumentResolvers:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite类型,参数解析器组合
  • initBinderArgumentResolvers:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite类型,DataBinder初始化方法(被@InitBinder注解)的参数解析器组合
  • returnValueHandlers:org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite类型,返回值处理器组合
  • requestResponseBodyAdvice:org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice和org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice类型的bean集合,在读request body或者写response body前后做一下特殊处理
  • messageConverters:org.springframework.http.converter.HttpMessageConverter类型的对象集合,从request或者response读取或写入信息

RequestMappingHandlerAdapter的核心逻辑如下图所示

创建DataBinder工厂(WebDataBinderFactory)

    DataBinder有两个作用:其一参数解析时做类型转换;其二参数解析时给对象设置属性(数据绑定)。这里只是创建DataBinder工厂,具体的DataBinder实例将在参数解析时根据需要由DataBinder工厂类创建。DataBinder工厂收集@InitBinder注解的方法,在创建DataBinder实例的时候依次执行这些方法达到初始化DataBinder的目的。@InitBinder可以用在2个地方。一个是@Controller注解的类中,一个是@ControllerAdvice注解的类中。收集@InitBinder就是从当前请求的Handler的类中找到@InitBinder注解的方法,以及所有@ControllerAdvice注解的类匹配当前请求的@InitBinder注解的方法。

     DataBinder工厂执行@InitBinder方法时也涉及到参数解析,所以也需要注册参数解析器。如果开发者没有给@InitBinder配置相应的参数解析器,Spring将加载默认的@InitBinder参数解析器。

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
... ...

    private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

		// 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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// Type-based argument resolution
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());

		// Custom arguments
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

		return resolvers;
	}
    ... ...
}

DataBinder工厂的实现类型创建DataBinder的源码如下:

public class DefaultDataBinderFactory implements WebDataBinderFactory {

    ... ...
    public final WebDataBinder createBinder(
			NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {

		WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
		if (this.initializer != null) {
			this.initializer.initBinder(dataBinder, webRequest);
		}
        //笔者注:执行所有@InitBinder方法,初始化dataBinder
		initBinder(dataBinder, webRequest);
		return dataBinder;
	}
    ... ...
}

初始化Model

创建Model工厂(ModelFactory)

与DataBinder工厂一样,Model工厂也会收集一些初始化Model的方法。两种方法将会被收集

1 请求Handler对应的@Controller类中被注解@ModelAttribute修饰且没有被注解@RequestMapping修饰的方法

2 @ControllerAdvice类被注解@ModelAttribute修饰且与请求Handler匹配的的方法

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
		Class<?> handlerType = handlerMethod.getBeanType();
		Set<Method> methods = this.modelAttributeCache.get(handlerType);
		if (methods == null) {

            //笔者注:请求Handler中@ModuleAttribute注解且没有@RequestMapping注解的方法
			methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
			this.modelAttributeCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
		// Global methods first
		this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
            
            //笔者注:@ControllerAdvice注解且匹配请求Handler类型的且有@ModuleAttribute的方法
			if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
				Object bean = controllerAdviceBean.resolveBean();
				for (Method method : methodSet) {
					attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
				}
			}
		});
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
		}
		return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
	}

初始化Model

初始化Model就是往Model中提前添加一些key-value。有3种情况

1 上一个请求通过FlashMap机制传递过来的参数,参考《FlashMap机制》

2 请求Handler关联的注解@SessionAttributes的session属性

3  Model工厂收集的@ModelAttribute方法,执行@ModelAttribute方法也需要参数解析,它的参数解析跟请求参数解析一致,解析器也是同一组解析器。
 

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
... ...
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
            ... ...
		    //笔者注:添加FalshMap机制传递的参数
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			//笔者注: 执行初始化逻辑
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            ... ...
            }
    ... ... 
}
public final class ModelFactory {
... ...
    public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
			throws Exception {
        //笔者注: @SessionAttributes注解的处理
		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		container.mergeAttributes(sessionAttributes);

        //笔者注:执行Model工厂收集的@ModelAttribute方法
		invokeModelAttributeMethods(request, container);

		for (String name : findSessionAttributeArguments(handlerMethod)) {
			if (!container.containsAttribute(name)) {
				Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
				if (value == null) {
					throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
				}
				container.addAttribute(name, value);
			}
		}
	}
... ...
}

 

请求解析参数

SpringMVC在执行请求的业务方法前解析request中的参数以匹配业务方法的形参。参数解析的工作由一个个的参数解析器完成。参数解析器org.springframework.web.method.support.HandlerMethodArgumentResolver接口的实现类。一般,系统在启动时注册多个参数解析器以解析不同的类型的参数。如果开发者没有配置解析器,那么SpringMVC将加载默认的解析器。加载的过程在RequestMappingHandlerAdapter的初始化过程中完成。源码如下:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
    ... ...
    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);
		}
        //笔者注:如没有设置DataBinder的参数解析器,加载默认解析器。DataBinder的初始化方法也是反射,也需要参数解析。DataBinder的参数解析少很多
		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);
		}
	}
... ...
}

默认注册的参数解析器如下,各个参数解析器将在后面的章节中详细介绍。

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
... ...
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

		// 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;
	}
... ...
}

HandlerMethodArgumentResolverComposite负责从已注册的参数解析器中选择适合当前参数类型的解析器,并利用选取的解析器解析参数。选择的逻辑如下:

 
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
... ...

// 笔者注:选择第一个适合当前参数的解析器
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
... ...

}

 

请求返回值处理

    请求的业务方法返回结果后,SpringMVC需要对结果进行处理。SpringMVC的返回值处理由返回处理器完成,返回处理器是org.springframework.web.method.support.HandlerMethodReturnValueHandler接口的实现类。一般,系统在启动时注册多个返回处理器以处理不同的类型的返回值。

    同参数解析器一样,如果开发者没有主动配置返回处理器,那么SpringMVC将加载默认的返回处理器,该过程也是在RequestMappingHandlerAdapter初始化的时候完成。默认注册的返回处理器如下,各个返回类型处理器将在后面的章节中详细介绍。

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
				this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

HandlerMethodReturnValueHandlerComposite负责从已注册的返回处理器中选择适合当前返回类型的处理器,并利用选取的处理器对返回值进行处理。选择的逻辑如下:

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {

	protected final Log logger = LogFactory.getLog(getClass());

	private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();

//笔者注:选择第一个支持的返回处理器
	@Nullable
	private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
    ... ...
}

返回ModelAndView

    DispatcherServlet后面的流程视图渲染需要的是ModelAndView对象,所以需要将结果包装成ModelAndView对象。ModelAndView包含两个属性:model、view。model存储了请求执行后的数据,而view则是告诉DispatcherServlet用哪个视图解析器解析并渲染view返回到客户端。

    在创建ModelAndView的时候有一点需要注意,如果model是org.springframework.web.servlet.mvc.support.RedirectAttributes类型,表明当前是重定向场景。此时,可能涉及到请求间的参数传递,需要将flashAttributes添加request的OUTPUT_FLASH_MAP属性中。详细情况参见《FlashMap机制-不同请求间传递参数》

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
... ...
    //笔者注:返回ModelAndView对象
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
        //重定向场景需要将falshAttributes添加到request的属性中。参见FlashMap
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
		}
		return mav;
	}
    ... ...
}

配置

RequestMappingHandlerAdapter配置形式与RequestMappingHandlerMapping一样。RequestMappingHandlerMapping支持的配置方式,RequestMappingHandlerAdapter也都支持。这里不在赘述。参考《RequestMappingHandlerMapping》

 



上一篇 RequestMappingHandlerMapping

下一篇 参数解析器HandlerMethodArgumentResolver

再下一篇 ModelAttribute使用方法

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值