文章目录
HandlerAdapter
- HandlerAdapter 的作用是封装 HandlerMapping 所获取到的 handler ,并调用 handler 处理清除,返回 ModelAndView 对象。
一、HandlerAdapter
1.1 HandlerAdapter设计思想?
- HandlerAdapter 是处理器适配器,它采用适配器模式来适配不同类型的 handler 请求处理器,这里首先思考为什么要这样设计,在 DispatchServlet 中,DispatchServlet直接获取到 handler 之后处理请求不行吗?为什么还需将 handler 包装成 HandlerAdapter 之后再进入处理请求的逻辑。这里是为了解耦,试想,假设没有 HandlerAdapter ,并且我们有三种 handler类型,那么三种类型来处理请求的方式各不一样,代码很可能是如下形式:
if(handler instanceof Servlet){
(Servlet)handler.service();
}else if(handler instanceof HandlerMethod){
(ServletInvocableHandlerMethod)handler.invokeAndHandle();
}else if (handler instanceof Controller){
((Controller) handler).handleRequest();
}
- 那当我们后续增加一种类型的时候呢?势必这段逻辑是需要修改的,违反开闭原则(对扩展开发,对修改关闭);反之有了HandlerAdapter之后,不管什么处理器,都需要实现 HandlerAdapter 的接口标准,DispatchServlet只需要找到支持 handler 的
HandlerAdapter就行了,然后调用 HandlerAdapter 中定义好的接口方法即可,扩展也是没有任何问题,这也是使用适配器模式的优势。
1.2 接口定义
- HandlerAdapter是一个接口,
public interface HandlerAdapter {
//HandlerAdapter 是否支持该handler
boolean supports(Object handler);
//HandlerAdapter 处理请求,返回一个ModelAndView
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
//和缓存有关,不支持的话可以简单返回-1
long getLastModified(HttpServletRequest request, Object handler);
}
1.3 初始化 HandlerAdapter
- 前面文章分析过,在 DispatcherServlet 的初始化策略的时候会初始化 HandlerAdapter,如下:
protected void initStrategies(ApplicationContext context) {
//省略 ...
initHandlerAdapters(context);
//省略 ...
}
- initHandlerAdapters : 方法会首先判断是否探测全部的HandlerAdapter,如果是,则则找出全部实现类,反之则找出指定实现类,如果没找到就加载默认策略。
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
//1.如果检测全部的HandlerAdapters,就找出全部匹配的
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
//2.如果不检测全部的,就找出指定的
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
//3.如果么有找到,就加载默认的
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}
- PS:
detectAllHandlerAdapters 对应一个配置,默认为true,加载全部 HandlerAdapter 类型的实例
1.4 获取 HandlerAdapter
- DispatcherServlet 在 doDispatch 方法中处理请求时获取 HandlerAdapter,通过 getHandlerAdapter 方法获取:
//DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//1.遍历初始化好的策略属性handlerAdapters
for (HandlerAdapter ha : this.handlerAdapters) {
//2.找到支持目标处理器的则返回
if (ha.supports(handler)) {
return ha;
}
}
//3.没有则抛出异常
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
- 代码流程很清晰,从策略属性 handlerAdapters 集合中遍历,寻找能够支持 handler 处理器的适配器,寻找到了就返回,最后也是由这个Adapter适配器去负责处理目标方法,加锁我们新增了一种 handler 处理器类型,那么针对该类的写一种适配器实现就行了,对于原本的代码没有任何影响。
二、HandlerAdapter 继承体系
- 下面是HandlerAdapter的子类继承关系
2.1 AbstractHandlerMethodAdapter
- 抽象类 , HandlerAdapter的抽象实现,支持 HandlerMethod
2.2 SimpleControllerHandlerAdapter
- 简单控制器处理器适配器,适配 Controller 类型的 handler。这里的控制器的实现是一个简单的控制器接口的实现,代码比较简单,核心方法如下。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
2.3 HttpRequestHandlerAdapter
- HTTP请求处理器适配器,适配 HttpRequestHandler 类型的处理器。它简单的将HTTP请求对象和响应对象传递给HTTP请求处理器的实现,不需要返回值,它主要应用于基于HTTP的远程调用的实现上,代码也比较简单,核心代码如下:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
2.4 SimpleServletHandlerAdapter
- 适配 Servlet 类型的 handler
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((Servlet) handler).service(request, response);
return null;
}
2.5 AnnotationMethodHandlerAdapter
- 已经被标记 @Deprecated ,就不关注了;
2.6 RequestMappingHandlerAdapter
- 非抽象类,继承自AbstractHandlerMethodAdapter , AbstractHandlerMethodAdapter的扩展、支持 @RequestMapping 的HandlerMethod , 虽然写在最后,但却是最需要关注的一个实现类;
三、RequestMappingHandlerAdapter 源码分析
3.1 AbstractHandlerMethodAdapter
- AbstractHandlerMethodAdapter 是接口的抽象骨架实现,定义了部分方法,代码不多,注释如下:
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
//最大数值代表最低优先级
private int order = Ordered.LOWEST_PRECEDENCE;
//构造方法
public AbstractHandlerMethodAdapter() {
// no restriction of HTTP methods by default
super(false);
}
//设置优先级
public void setOrder(int order) { this.order = order; }
@Override
public int getOrder() { return this.order; }
//判断适配器是否支持对应的Handler处理器方法
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
//子类重写,判断是否支持对应的处理器方法
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
//处理请求,注意参数的handler期望是一个HandlerMethod
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//子类重写,处理的核心方法
@Nullable
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);
}
//子类重写,getLastModifiedInternal
protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
}
- 查看 AbstractHandlerMethodAdapter 可知子类需要实现两个关键的模板方法:supportsInternal 和 handleInternal ;
- RequestMappingHandlerAdapter 是 HandlerAdapter 最复杂的一个子类,也是最需要关注分析的子类,源码也较多,从 AbstractHandlerMethodAdapter 出发,我们关注几个需要重写的三个方法(都标记了子类重写),在 RequestMappingHandlerAdapter 中 supportsInternal 和 getLastModifiedInternal 都是简单实现,前者返回true,后者返回-1。因此主要分析 handleInternal 方法。
3.2 supportsInternal
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
- supportsInternal 返回true表示,只要处理器类型是 HandlerMethod类型,RequestMappingHandlerAdapter 就能够支持;
3.3 handleInternal
- handleInternal 是 RequestMappingHandlerAdapter 处理请求的注意逻辑,大体分为下面三步:
1.参数解析(解析参数,比较复杂)
2.使用处理器处理请求 (反射调用handleMethod )
3.处理返回值,(将不同类型的返回值统一处理成ModelAndView)
- RequestMappingHandlerAdapter#handleInternal,我们先从整体上来看,方法的作用就是处理请求,然后返回一个 ModelAndView。
//RequestMappingHandlerAdapter#handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//1.校验请求方法检查是否支持
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 需要对 session 进行同步处理
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
//不需要对 session 进行同步处理就直接对 HandlerMethod 进行处理
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 请求头没有Cache-Control,直接返回,包含则需要处理
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
//返回模型示图
return mav;
}
- handleInternal 主要做了两部分处理:一个是判断当前是否对session进行同步处理,如果需要,则对其调用进行加锁,反之直接调用;另一个是判断请求头中是否包含Cache-Control请求头,如果不包含则设置其Cache立即失效。对于 HandlerMethod 的具体处理是在 invokeHandlerMethod 方法中进行的;
3.4 invokeHandlerMethod
- invokeHandlerMethod 是处理请求的主体逻辑,对于 HandlerMethod 的调用处理:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//1.获取全局的 InitBinder 和局部的 InitBinder,用于参数绑定, 这些方法用于参数绑定
//注意全局是在 @ControllerAdvice 类中声明的 ,局部的是目标处理类中声明的,比如@Controller中声明的,
//具体可以参考4.2示例和第五大点初始化部分代码解析
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//2.获取容器中全局的 ModelAttribute 和局部的 ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
//注意全局是在 @ControllerAdvice 类中声明的,局部的是目标处理类中声明的,比如@Controller中声明的,
//具体可以参考4.2示例和第五大点初始化部分代码解析
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//3.将 handlerMethod 封装为一个 ServletInvocableHandlerMethod 对象, 该对象用于对当前request的整体调用流程进行了封装
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
//4.设置当前容器中配置的所有 ArgumentResolver 参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
//5.设置当前容器中配置的所有 ReturnValueHandler 返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//6.将 WebDataBinderFactory 设置到 ServletInvocableHandlerMethod 中
invocableMethod.setDataBinderFactory(binderFactory);
//7.设置 ParameterNameDiscoverer ,用于发现参数的名称
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//8.initModel() 方法调用前面获取到的 @ModelAttribute 标注的方法,使@ModelAttribute标注的方法能够在目标Handler之前调用
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//9.获取当前的AsyncWebRequest,判断目标 handler 的返回值是否为 WebAsyncTask 或 DefferredResult
//如果是二者之一则当前请求的处理是异步的。当前请求会将Controller中封装的业务逻辑放到一个线程池中
//进行调用,待该调用有返回结果之后再返回到response中。
//异步的优点在于解放请求分发的线程,从而处理更多的请求,只有目标任务完成后才会回来将该异步任务结果返回
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
//10.封装异步任务的线程池,request 和 interceptors 到 WebAsyncManager中
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
//11.判断当前请求是否有异步任务结果,如果有结果则对异步任务结果进行封装
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//12.对请求参数进行处理,调用目标 HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//13.对封装的 ModelAndView 进行处理,判断当前请求是否进行了重定向,如果进行了重定向,
//还会判断是否需要将 FlashAttributes 封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
//14.调用request destruction callbacks和对SessionAttributes进行处理
webRequest.requestCompleted();
}
}
- invokeHandlerMethod 的大体处理流程包括 :
1.获取 @InitBinder注解的的参数绑定转换器
2.获取当前容器中使用@ModelAttribute标注但没有使用 @RequestMapping 标注的方法,并且在调用目标方法之前调用这些方法,参考4.2小结和第五点初始化部分代码解析
3.判断目标 handler 返回值是否使用了 WebAsyncTask 或 DefferredResult 封装,如果封装了,则按照异步任务的方式进行执行;
4.处理请求参数,调用目标方法和处理返回值。
- 这里面的代码注释大部分是参考了参考文章[4]的
3.5 getDataBinderFactory
- getDataBinderFactory 处理标注@InitBinder的方法,用于参数绑定
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
//1.缓存中获取当前 handler 所需要的 InitBinder 方法,第一次缓存是没有的,这个缓存在Bean的生命周
//期不会初始化,只会在第一次访问的时候初始化好,后续使用就直接用缓存
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
//2.缓存未找到,就扫描 handlerType 类型的Bean去寻找注解了 @InitBinder 的方法,因此此类方法需
//要在目标处理类里面声明,参考 4.1
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
//2.得到全部的 initBinderMethods 方法,先添加全局的,全局的是 @ControllerAdvice 类里面去找,不过这里全
//局的已经初始化好在 initBinderAdviceCache 属性里面,只需要遍历处理,至于全局的初始化时机参考第五点的分析
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
//3.再添加 HandlerMethod 所在Bean中的 InitBinder 方法,即局部的 InitBinder
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
//4.将 InitBinder 封装到 InitBinderDataBinderFactory 中
return createDataBinderFactory(initBinderMethods);
}
-
getDataBinderFactory 方法主要用于参数绑定相关,内部会获取全局和局部两种类型的 InitBinder ,全局类型的 InitBinder 需要在类上使用@ControllerAdvice进行标注,并且声明方法上使用 @InitBinder 进行标注;局部的是当前 handler 所在类中的使用 @InitBinder 标注的方法(比如Controller中的)。这两种InitBinder都会执行,只不过全局类型的InitBinder会先于局部类型的InitBinder执行。
-
参数绑定这块代码还是有点晦涩的,为此在后面搭配了一个简单的示例,在 4.1 的自定义参数绑定,结合调试看的会更清晰点。
3.6 getModelFactory
- getModelFactory方法会获取 @ModelAttribute 标注的方法,此类方法会在目标方法之前执行; (关于 @ModelAttribute 的方法可以参考4.2中的示例)
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
//1.和获取绑定方法套路一样,先从缓存获取,第一次没有缓存,后续都可以使用缓存加快速度
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
//2.先处理全局的
// Global methods first
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
//3.再到目标Bean处理局部的
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
- 和前面的 InitBinder 类似,全局的在 @ControllerAdvice 中,局部的是处理器所在的类中,全部找出包装之后返回。
四、示例
4.1 自定义参数绑定
- 自定义一个局部的参数绑定器, 代码如下,其实就是将传来的参数按照自己的方式用冒号分割,然后赋给属性:
//参数类型
@Data
public class Person {
private String username;
private String address;
}
//控制器,接受 Person 类型的参数
@RestController
public class BinderTestController {
@RequestMapping("/binderTest")
public String binderTest(@RequestParam("person") Person person) {
String result = person.toString() + " " + new Date();
System.out.println(result);
return result;
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Person.class, new PersonEditor());
}
}
//参数绑定,将参数分割,赋值给 Person
public class PersonEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
Person p = new Person();
if (text != null) {
String[] items = text.split(":");
p.setUsername(items[0]);
p.setAddress(items[1]);
}
setValue(p);
}
@Override
public String getAsText() {
return getValue().toString();
}
}
测试:
测试输入:http://localhost:8080/binderTest?person=mozping:shenzhen,即可
打印并返回:Person(username=mozping, address=shenzhen) Fri Nov 15 15:28:19 CST 2019
- 调试图:
- 图中可以看到,寻找到的 handler 类型就是控制器 BinderTestController,寻找到的 methods 只有一个,就是 initBinder,没有全局的@InitBinder,因此全局的绑定方法是空,最后只找到我们自定义的这个。
4.2 自定义ModelAttribute方法
- 自定义全局和局部的 @ModelAttribute方法,下面是局部的,ModelAttribute 方法会在控制器的其他方法之前执行,在3.4的 invokeHandlerMethod 方法中解释过了,我们看代码
@RestController
public class ModelAttributeController {
@ModelAttribute
public void myModel(Model model) {
System.out.println("ModelAttribute method ------------ ");
}
@RequestMapping("/modelAttr")
public String modelAttr(@RequestParam("name") String name) {
System.out.println("modelAttr execute ... " + name);
return "modelAttr";
}
}
- @ControllerAdvice 声明定义全局的 @ModelAttribute 方法:
//然后是一个全局的 @ModelAttribute 方法,必须使用 @ControllerAdvice 声明
@ControllerAdvice
public class GlobalModelAttribute {
@ModelAttribute
public void myModel(Model model) {
System.out.println("Global ModelAttribute method ------------ ");
}
}
请求:http://localhost:8080/modelAttr?name=mozping
打印:
Global ModelAttribute method ------------
ModelAttribute method ------------
modelAttr execute ... mozping
- 可以看到 @ModelAttribute 的方法会在目标方法之前被执行,注意如果有多个 @RequestMapping 方法,@ModelAttribute都会在前面拦截执行,需要谨慎使用。
- 非全局的 @ModelAttribute 方法必须在目标处理器类里面声明,框架也是从 bean里面去寻找的,全局的则需要使用 @ControllerAdvice 声明,全局的会优先执行
- 在 @ModelAttribute 方法的参数中有一个 Model 形参,因此可以对示图做一些修改,更多关于 @ModelAttribute 可以阅读参考文章[5]
五、RequestMappingHandlerAdapter 初始化
-
前面的 @InitBinder 和 @ModelAttribute 都有全局和局部的,局部的就是去Bean里面去找,而全局的直接遍历initBinderAdviceCache 和 modelAttributeAdviceCache 属性集合处理,这些全局的在Bean的生命周期节点就已经扫描并保存好了。
-
RequestMappingHandlerAdapter 初始化部分放在最后,前面我们发现@InitBinder 和 @ModelAttribute 的扫描过程很类似,都是有全局的和局部的,局部的就定义在目标处理类的内部,代码通过到目标Bean去扫描获取,而全局的则定义在由 @ControllerAdvice 注解的类中,全局的 @InitBinder 和 @ModelAttribute分别缓存在属性 initBinderAdviceCache 和 modelAttributeAdviceCache 中,这两个缓存的集合在 Bean 初始化的时候就会去扫描并保存起来, 通过 initControllerAdviceCache() 方法来扫描,时机是在
InitializingBean接口的 afterPropertiesSet 方法,代码如下:
5.1 执行时机
- InitializingBean#afterPropertiesSet方法的执行时机在属性赋值完毕之后。
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//将全局的 @ControllerAdvice 类扫描进来,里面可能有全局的参数绑定器和 @ModelAttribute 方法
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);
}
}
5.2 扫描加载
- initControllerAdviceCache:加载全局的参数绑定器和@ModelAttribute方法,核心代码如下:
private void initControllerAdviceCache() {
//1.找出全部@ControllerAdvice 的Bean,并排序好
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
//2.依次遍历处理
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
//3.寻找不包含 RequestMapping注解 并且包含 ModelAttribute 注解 的方法
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
//4.不为空则将 ModelAttribute方法添加到缓存
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
//5.处理参数绑定器,寻找包含 @InitBinder 注解的方法
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
//6.不为空则将 InitBinder参数绑定方法添加到缓存
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
//7.requestResponseBodyAdviceBeans集合添加 RequestBodyAdvice 类型的Bean
if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
//8.requestResponseBodyAdviceBeans集合添加 RequestBodyAdvice 类型的Bean
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}
- 下面想寻找全局增强的 ControllerAdvice 类型Bean的逻辑,很清晰,全局的 @InitBinder 和 @ModelAttribute 都是在 @ControllerAdvice 类里面去扫描的;
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
List<ControllerAdviceBean> beans = new ArrayList<>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
beans.add(new ControllerAdviceBean(name, applicationContext));
}
}
return beans;
}
- @InitBinder方法 的加载条件是:包含@InitBinder注解
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
- @ModelAttribute方法 的加载条件是:包含 @ModelAttribute 注解且不包含 @RequestMapping 注解
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
六、小结
-
- 全局的 @InitBinder 和 @ModelAttribute 都是在 @ControllerAdvice 类里面去寻找的,
-
- @InitBinder 则是寻找 @ControllerAdvice 里面注解了@InitBinder的方法;
-
- @ModelAttribute 则是寻找 @ControllerAdvice 里面注解了@ModelAttribute并且没有注解 RequestMapping的方法
-
- 局部的 @InitBinder 和 @ModelAttribute 都是在目标处理器内部去寻找的,注解过滤要求是一样的
-
- 全局的优先于局部的先执行
- 文章主要是梳理了 RequestMappingHandlerAdapter 的执行流程,配合示例重点看了参数绑定器和 @ModelAttribute的初始化以及加载时机和加载逻辑。
- RequestMappingHandlerAdapter 的执行流程大体上如下:
1.session同步处理
2.目标方法调用
( 获取@InitBinder和@ModelAttribute -> 设置参数处理器 ->
设置返回值处理器 -> 参数发现器 -> 调用 @ModelAttribute 方法
-> 目标方法调用 -> 结果处理和返回 )
3.Cache-Control 处理
- 在 getDataBinderFactory 和 getModelFactory 方法里面分别有createInitBinderMethod 和 createModelAttributeMethod 方法来包装目标的@InitBinder 和 @ModelAttribute方法,二者都是返回 InvocableHandlerMethod 对象,他是 HandlerMethod 对象,也就是封装了目标方法的类,createModelAttributeMethod 和 createModelAttributeMethod 里面都涉及到 HandlerMethodArgumentResolver 处理器方法参数解析器策略,以及返回值处理器HandlerMethodReturnValueHandler等,在后续文章再分析。