springmvc第五篇 --HandlerAdapter

本文详细介绍了SpringMVC中的HandlerAdapter,尤其是RequestMappingHandlerAdapter的内部结构、工作流程和参数绑定机制。RequestMappingHandlerAdapter是最复杂的组件,负责处理handler方法,包括参数准备、执行处理器请求和处理返回值。参数来源包括请求、cookie、session等多个方面,通过HandlerMethodArgumentResolver组件完成。同时,文章探讨了如何初始化和使用RequestMappingHandlerAdapter,以及涉及的ModelAndViewContainer、ModelFactory等关键概念。
摘要由CSDN通过智能技术生成

HandlerAdapter系列

上一篇讲到了HandlerMapping通过request找到了handler,HandlerAdapter是具体使用handler干活的,每一个封装了HandlerAdapter的具体使用方法。下面看一下结构图。
结构图
从图中看到,一共有五个类,只有RequestMappingHandlerAdapter有两层,别的都是直接实现handlerMapping接口。在这几个类中,RequestMappingHandlerAdapter是最复杂的,其他三个则非常简单,因为其他的三个类格式都是很固定的。

HttpRequestHandlerAdapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}


SimpleServletHandlerAdapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((Servlet) handler).service(request, response);
		return null;
	}

SimpleControllerHandlerAdapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}

这三个adapter非常简单,这里就不多说了接下来看一下AbstractHandlerMethodAdapter。

RequestMappingHandlerAdapter概述

RequestMappingHandlerAdapter继承与AbstractHandlerMethodAdapter中,AbstractHandlerMethodAdapter中有三个模板方法。supportsInternal,handleInternal和getLastModified,在supports方法中还增加了一个条件,handler必须是HandlerMethod类型。

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}
	protected abstract ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
	@Override
	public final long getLastModified(HttpServletRequest request, Object handler) {
		return getLastModifiedInternal(request, (HandlerMethod) handler);
	}
		protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
}

接下来看一下RequestMappingHandlerAdapter,RequestMappingHandlerAdapter可以说是整个springmvc最复杂的组件,他的supportsInternal直接返回true,也就是不增加判断handler的条件,只需要满足父类的要求就可以了,getLastModifiedInternal直接返回-1,最重要的是handleInternal,这个就是实际处理handler的方法。具体大致分为三步:

  • 准备参数
  • 使用处理器请求
  • 处理返回值,也就是讲不同的类型返回值同意处理成ModelAndView类型。

最简单的是第二步,第三步还好,最麻烦的是第一步,也就是参数准备工作,这一步根据处理器设置参数,而参数的类型,和个数都是不确定的。所以难度非常大,另外这个过程中只用了大量的组件,这也是增加难度的原因。
要想理解参数的绑定得需要先明白三个问题:

  • 都有哪些参数需要绑定
  • 参数值的来源
  • 具体进行绑定的方法
    需要绑定的参数当然是处理器的参数 不过这里除了方法的参数外还有其他两个方法的参数需要绑定,那就是@modelAttribure和@InitBinder方法,
    参数来源有6个:
    request中相关得到参数,url中的参数,post过来的参数以及请求头中过来的值
    cookie中的参数
    session中的参数
    设置到flashMap中参数
    sessionAttribute中的参数
    modelAttribute中的参数
    参数是通过HandlerMethodArgumentResolver类型组件完成的,不同类型使用不同HandlerMethodArgumen来完成。

RequestMappingHandlerAdapter自身结构

RequestMappingHandlerAdapter的创建在afterPropertiesSet方法进行实现,其内容是初始化argumentResolvers,initBinderArgumentResolvers,returnValueHandlers以及@ControllerAdrivce注释相关的modelAttributeAdviceCache,initBinderAdviceCache和requestResponseBodyAdvice着几个属性:
argumentResolvers:用于给处理器方法和注释了@ModelAttribute的方法设置参数
initBinderArgumentResolvers:用于给注释了@InitBinder的方法设置参数
returnValueHandlers:用于将处理的的返回值设置为ModelAndView类型
modelAttributeAdviceCache和initBinderAdviceCache:用于缓存注释了 @ControllerAdvice注解的类里面注释了@ModelAttribute和@IntiBinder的方法。每个类自己的@modelAttribute和@initBinder的方法是在第一次使用了处理器处理请求的时候缓存起来。
requestResponseBodyAdvice:用于前面提到过的实现了ResponseBodyAdvice和RequestBodyAdvice接口的类。
把上面的这些都理解清楚,在分析RequestMappingHandlerAdapter组件就会容易多了。需要注意的是,这些属性是复数形式,就是有多个,在使用的时候会按照顺序调用,所以在初始化的时候顺序就很重要了,大家要留意一下,下面看afterPropertiesSet的代码。

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
public void afterPropertiesSet() {
		// 初始化注释了@ControllerAdvice的类的相关属性
		initControllerAdviceCache();
	
		//初始化argumentResolvers属性
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		//初始化initBinderArgumentResolvers属性
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		//初始化returnValueHandlers属性
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

上面的四步非常的清晰。首先initControllerAdviceCache初始化了@ControllerAdvice属性,然后依次初始化了argumentResolvers,initBinderArgumentResolvers,returnValueHandlers,后面的三个属性的初始化都一样,都先调用了getDefaultArgumentXXX的方法得到了相应的值,然后设置给对应的属性。下面先看一下initControllerAdviceCache是怎么工作的。

private void initControllerAdviceCache() {

		//先获取容器为空就直接返回
		if (getApplicationContext() == null) {
			return;
		}
		//获取所有注释了ControllerAdvice的bean
		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		//根据order排序
		AnnotationAwareOrderComparator.sort(adviceBeans);

		List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
	
		
		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
			//查找注释了@ModelAttribute且没有注释@ReqestMapping的方法		    
			MODEL_ATTRIBUTE_METHODS是一个MethodFilter类型的过滤器,前面已经提到过
		
			Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
			if (!attrMethods.isEmpty()) {
				this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
			}

			//查找注释了@InitBinder的方法
			Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
			if (!binderMethods.isEmpty()) {
				this.initBinderAdviceCache.put(adviceBean, binderMethods);
			}
			//查找实现了RequestBodyAdvice的类
			if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
				requestResponseBodyAdviceBeans.add(adviceBean);
			}
			//查找实现了ResponseBodyAdvice的类
			if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				requestResponseBodyAdviceBeans.add(adviceBean);
			}
		}
	
		//将查找实现了RequestBodyAdvice 和 ResponseBodyAdvice的类添加到requestResponseBodyAdvice中
		if (!requestResponseBodyAdviceBeans.isEmpty()) {
			this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
		}

		if (logger.isDebugEnabled()) {
			int modelSize = this.modelAttributeAdviceCache.size();
			int binderSize = this.initBinderAdviceCache.size();
			int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
			int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
			if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
				logger.debug("ControllerAdvice beans: none");
			}
			else {
				logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
						" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
			}
		}
	public static final MethodFilter INIT_BINDER_METHODS = method ->
			AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);

	public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
			(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
					AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));

这里首先通过ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());拿到了所有注释了ControllerAdvice的类,并根据order排序,然后遍历这些bean,查找注释了@ModelAttribute和@InitBinder的方法 ,查找@ModelAttribute和@InitBinder的方法是根据MethodIntrospector.selectMethods,是根据第二个参数filter来选择的,然后添加到对应的modelAttributeAdviceCache和initBinderAdviceCache中。

实现了RequestBodyAdvice和ResponseBodyAdvice的类并不是直接添加到requestResponseBodyAdvice中的,而是先保存到临时变量requestResponseBodyAdviceBeans中的,这里做的目的是把找到的requestResponseBodyAdvice放到最前面。

说完initControllerAdviceCache,在看一下那三个getDefaultArgumentXXX方法,这三个非常类似,下面以getDefaultArgumentResolvers来分析。

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

		// 添加按注释解析参数的解析器
		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 RequestResponseBodyMeth
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值