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