06-HandlerAdapter

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));

六、小结

    1. 全局的 @InitBinder 和 @ModelAttribute 都是在 @ControllerAdvice 类里面去寻找的,
    1. @InitBinder 则是寻找 @ControllerAdvice 里面注解了@InitBinder的方法;
    1. @ModelAttribute 则是寻找 @ControllerAdvice 里面注解了@ModelAttribute并且没有注解 RequestMapping的方法
    1. 局部的 @InitBinder 和 @ModelAttribute 都是在目标处理器内部去寻找的,注解过滤要求是一样的
    1. 全局的优先于局部的先执行
  • 文章主要是梳理了 RequestMappingHandlerAdapter 的执行流程,配合示例重点看了参数绑定器和 @ModelAttribute的初始化以及加载时机和加载逻辑。
  • RequestMappingHandlerAdapter 的执行流程大体上如下:
1.session同步处理
2.目标方法调用 
( 获取@InitBinder@ModelAttribute -> 设置参数处理器 -> 
				设置返回值处理器 -> 参数发现器 -> 调用 @ModelAttribute 方法 
											-> 目标方法调用 -> 结果处理和返回 )
3.Cache-Control 处理 
  • 在 getDataBinderFactory 和 getModelFactory 方法里面分别有createInitBinderMethod 和 createModelAttributeMethod 方法来包装目标的@InitBinder 和 @ModelAttribute方法,二者都是返回 InvocableHandlerMethod 对象,他是 HandlerMethod 对象,也就是封装了目标方法的类,createModelAttributeMethod 和 createModelAttributeMethod 里面都涉及到 HandlerMethodArgumentResolver 处理器方法参数解析器策略,以及返回值处理器HandlerMethodReturnValueHandler等,在后续文章再分析。

七、参考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章