精盡Spring MVC源碼分析 – HandlerAdapter 組件(一)之 HandlerAdapter

該系列文檔是本人在學習 Spring MVC 的源碼過程中總結下來的,可能對讀者不太友好,請結合我的源碼注釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀

Spring 版本:5.2.4.RELEASE

該系列其他文檔請查看:《精盡 Spring MVC 源碼分析 – 文章導讀》

HandlerAdapter 組件

HandlerAdapter 組件,處理器的適配器。因為處理器 handler 的類型是 Object 類型,需要有一個調用者來實現 handler 是怎麼被執行。Spring 中的處理器的實現多變,比如用戶的處理器可以實現 Controller 介面或者 HttpRequestHandler 介面,也可以用 @RequestMapping 註解將方法作為一個處理器等,這就導致 Spring MVC 無法直接執行這個處理器。所以這裡需要一個處理器適配器,由它去執行處理器

由於 HandlerMapping 組件涉及到的內容較多,考慮到內容的排版,所以將這部分內容拆分成了五個模組,依次進行分析:

  • 《HandlerAdapter 組件(一)之 HandlerAdapter》
  • 《HandlerAdapter 組件(二)之 ServletInvocableHandlerMethod》
  • 《HandlerAdapter 組件(三)之 HandlerMethodArgumentResolver》
  • 《HandlerAdapter 組件(四)之 HandlerMethodReturnValueHandler》
  • 《HandlerAdapter 組件(五)之 HttpMessageConverter》

HandlerAdapter 組件(一)之 HandlerAdapter

先來回顧一下在 DispatcherServlet 中處理請求的過程中哪裡使用到 HandlerMapping 組件,可以回到《一個請求的旅行過程》中的 DispatcherServletdoDispatch 方法中看看,如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   ModelAndView mv = null;
   // ... 省略相關程式碼
   // <3> 獲得請求對應的 HandlerExecutionChain 對象(HandlerMethod 和 HandlerInterceptor 攔截器們)
   mappedHandler = getHandler(processedRequest);
   // ... 省略相關程式碼
   // <4> 獲得當前 handler 對應的 HandlerAdapter 對象
   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
   // ... 省略相關程式碼
   // <6> 真正的調用 handler 方法,也就是執行對應的方法,並返回視圖
   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
   // ... 省略相關程式碼
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
       for (HandlerAdapter adapter : this.handlerAdapters) {
           if (adapter.supports(handler)) {
               return adapter;
           }
       }
   }
   throw new ServletException("No adapter for handler [...");
}

通過遍歷 HandlerAdapter 組件們,判斷是否支援處理該 handler 處理器,支援則返回該 HandlerAdapter 組件。注意,這裡是通過一個一個的 HandlerAdapter 組件去判斷是否支援該處理器,如果支援則直接返回這個 HandlerAdapter 組件,不會繼續下去,所以獲取處理器對應 HandlerAdapter 組件是有一定的先後順序的,默認是HttpRequestHandlerAdapter -> SimpleControllerHandlerAdapter -> RequestMappingHandlerAdapter

本文涉及到的內容適中,可以先查看我的總結

HandlerAdapter 介面

org.springframework.web.servlet.HandlerAdapter介面,處理器的適配器,去執行處理器,程式碼如下:

public interface HandlerAdapter {
	/**
	 * 是否支援該處理器
	 */
	boolean supports(Object handler);

	/**
	 * 執行處理器,返回 ModelAndView 結果
	 */
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	/**
	 * 返回請求的最新更新時間,如果不支援該操作,則返回 -1 即可
	 */
	long getLastModified(HttpServletRequest request, Object handler);
}

HandlerAdapter 介面的體系結構如下:

沒有特別多😈 心裡有點點欣慰,其中 RequestMappingHandlerAdapter 就是基於@RequestMapping 等註解的 HandlerMethod 的 HandlerMethodAdapter 實現類,名字都差不多

初始化過程

DispatcherServletinitHandlerAdapters(ApplicationContext context) 方法,會在 onRefresh 方法被調用,初始化 HandlerAdapter 組件,方法如下:

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    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 {
        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.
        }
    }

    // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    /**
     * 如果未獲得到,則獲得默認配置的 HandlerAdapter 類
     * {@link org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter}
     * {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter}
     * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter}
     */
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}
  1. 如果「開啟」探測功能,則掃描已註冊的 HandlerAdapter 的 Bean 們,添加到 handlerAdapters 中,默認開啟,這裡會進行排序,可以通過實現 Order 介面設置排序值

  2. 如果「關閉」探測功能,則獲得 Bean 名稱為 “handlerAdapter” 對應的 Bean ,將其添加至 handlerAdapters

  3. 如果未獲得到,則獲得默認配置的 HandlerAdapter 類,調用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是從 DispatcherServlet.properties 文件中讀取 HandlerAdapter 的默認實現類,如下:

    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    

    可以看到對應的是 HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter 三個實現類,接下來就一個一個分析

HttpRequestHandlerAdapter

org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,實現 HandlerAdapter 介面,基於 HttpRequestHandler 介面的 HandlerAdapter 實現類,程式碼如下:

public class HttpRequestHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		// 判斷是 HttpRequestHandler 類型
		return (handler instanceof HttpRequestHandler);
	}
    
	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// HttpRequestHandler 類型的調用
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}
    
	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		// 處理器實現了 LastModified 介面的情況下
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}

//  org.springframework.web.HttpRequestHandler.java
@FunctionalInterface
public interface HttpRequestHandler {
	/**
	 * 處理請求
	 */
	void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException;
}

邏輯比較簡單,如果這個處理器實現了 HttpRequestHandler 介面,則使用 HttpRequestHandlerAdapter 調用該處理器的 handleRequest(HttpServletRequest request, HttpServletResponse response) 方法去處理器請求,返回 null

這種處理器如何配置呢?

可以回到《HandlerMapping 組件(四)之 AbstractUrlHandlerMapping》SimpleUrlHandlerMapping 或者 BeanNameUrlHandlerMapping 小節中的使用示例看看

SimpleControllerHandlerAdapter

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,實現 HandlerAdapter 介面,基於 Controller 介面的 HandlerAdapter 實現類,程式碼如下:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		// <1> 判斷是 Controller 類型
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// <2> Controller 類型的調用
		return ((Controller) handler).handleRequest(request, response);
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		// 處理器實現了 LastModified 介面的情況下
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}

@FunctionalInterface
public interface Controller {
	/**
	 * 處理請求
	 */
	@Nullable
	ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

邏輯比較簡單,和 HttpRequestHandlerAdapter 差不多,如果這個處理器實現了 Controoler 介面,則使用 HttpRequestHandlerAdapter 調用該處理器的 handleRequest(HttpServletRequest request, HttpServletResponse response) 方法去處理器請求,直接返回處理器執行後返回 ModelAndView

這種處理器如何配置和 HttpRequestHandlerAdapter 相同,見上文描述

SimpleServletHandlerAdapter 實現類就不講述了,因為默認的 HandlerAdapter 實現類中沒有它

邏輯實現和 SimpleControllerHandlerAdapter 差不多,區別在於它判斷是否為 javax.servlet.Servlet 對象,是的話則調用其 service方法,返回該方法執行後返回的 ModelAndView 對象

AbstractHandlerMethodAdapter

org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter,實現 HandlerAdapter、Ordered 介面,繼承 WebContentGenerator 抽象類,基於 HandlerMethod 的 HandlerMethodAdapter 抽象類

構造方法

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
    /** 最低優先順序 */
	private int order = Ordered.LOWEST_PRECEDENCE;

	public AbstractHandlerMethodAdapter() {
		// no restriction of HTTP methods by default
		// 調用 WebContentGenerator 類的構造方法
	    // 參數 restrictDefaultSupportedMethods 參數為 false ,表示不需要嚴格校驗 HttpMethod
		super(false);
	}
}

supports方法

實現 supports(Object handler) 方法,判斷是否支援該處理器,程式碼如下:

@Override
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
  • 處理器必須是 HandlerMethod 類型,也就是在《HandlerMapping 組件(三)之 AbstractHandlerMethodMapping》講到的通過 @RequestMapping 等註解方法所生成對應 HandlerMethod 對象
  • 還需要調用抽象方法 supportsInternal(HandlerMethod handlerMethod)判斷是否支援, 交由子類去實現,詳情見下文

handle方法

實現 handle(HttpServletRequest request, HttpServletResponse response, Object handler) 方法,用於處理請求,執行該處理器,程式碼如下:

@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;
  • 如果該 HandlerAdapter 支援這個處理器,那麼則會調用該方法去處理請求,執行這個處理器
  • 直接調用 handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) 抽象方法,交由子類去實現,詳情見下文

getLastModified方法

實現 getLastModified(HttpServletRequest request, Object handler) 方法,獲得最後更新時間,程式碼如下

@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
    return getLastModifiedInternal(request, (HandlerMethod) handler);
}

protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
  • 直接調用getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod)抽象方法,交由子類去實現,詳情見下文

RequestMappingHandlerAdapter

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,實現 BeanFactoryAware、InitializingBean 介面,繼承 AbstractHandlerMethodAdapter 抽象類,基於 @RequestMapping 註解的 HandlerMethod 處理器的 HandlerMethodAdapter 實現類

構造方法

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {

	/**
	 * MethodFilter that matches {@link InitBinder @InitBinder} methods.
	 */
	public static final MethodFilter INIT_BINDER_METHODS = method ->
			AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
	/**
	 * MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
	 */
	public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
			(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
					AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));

	@Nullable
	private List<HandlerMethodArgumentResolver> customArgumentResolvers;

	@Nullable
	private HandlerMethodArgumentResolverComposite argumentResolvers;

	@Nullable
	private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;

	@Nullable
	private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;

	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

	@Nullable
	private List<ModelAndViewResolver> modelAndViewResolvers;

	private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();

	private List<HttpMessageConverter<?>> messageConverters;

	private List<Object> requestResponseBodyAdvice = new ArrayList<>();

	@Nullable
	private WebBindingInitializer webBindingInitializer;

	private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");

	@Nullable
	private Long asyncRequestTimeout;

	private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0];

	private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0];

	private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();

	private boolean ignoreDefaultModelOnRedirect = false;

	private int cacheSecondsForSessionAttributeHandlers = 0;

	/**
	 * 是否對相同 Session 加鎖
	 */
	private boolean synchronizeOnSession = false;

	private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();

	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

	@Nullable
	private ConfigurableBeanFactory beanFactory;

	// ========== 快取 ==========
	private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);
	private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
	private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
	private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
	private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();
    
    // ... 省略 getter、setter 方法
	public RequestMappingHandlerAdapter() {
		// 初始化 messageConverters
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

		this.messageConverters = new ArrayList<>(4);
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(stringHttpMessageConverter);
		try {
			this.messageConverters.add(new SourceHttpMessageConverter<>());
		}
		catch (Error err) {
			// Ignore when no TransformerFactory implementation is available
		}
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}
}

有許多的屬性,不著急理解,先列幾個主要的屬性對象:

  • HandlerMethodArgumentResolverComposite argumentResolvers:參數處理器組合對象
  • HandlerMethodReturnValueHandlerComposite returnValueHandlers:返回值處理器組合對象
  • List<HttpMessageConverter<?>> messageConverters:HTTP 消息轉換器集合對象
  • List<Object> requestResponseBodyAdvice: RequestResponseAdvice 集合對象

在構造方法中默認會添加了四個 HttpMessageConverter 對象,當然,默認還會添加其他的,例如 MappingJackson2HttpMessageConverter 為 JSON 消息格式的轉換器

1.afterPropertiesSet 初始化方法

因為 RequestMappingHandlerAdapter 實現了 InitializingBean 介面,在 Sping 初始化該 Bean 的時候,會調用該方法,完成一些初始化工作,方法如下:

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    // <1> 初始化 ControllerAdvice 相關
    initControllerAdviceCache();

    // <2> 初始化 argumentResolvers 屬性
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // <3> 初始化 initBinderArgumentResolvers 屬性
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // <4> 初始化 returnValueHandlers 屬性
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}
  1. 調用 initControllerAdviceCache() 方法,初始化 ControllerAdvice 相關,詳情見下文

  2. 初始化 argumentResolvers 屬性,調用 getDefaultArgumentResolvers() 方法,獲得默認的 HandlerMethodArgumentResolver 數組,詳情見下文

  3. 初始化 initBinderArgumentResolvers 屬性,調用 getDefaultInitBinderArgumentResolvers() 方法,獲得默認的 HandlerMethodArgumentResolver 數組,詳情見下文

  4. 初始化 returnValueHandlers 屬性,調用 getDefaultReturnValueHandlers() 方法,獲得默認的 HandlerMethodReturnValueHandler 數組,詳情見下文

1.1 initControllerAdviceCache

initControllerAdviceCache() 方法,初始化 ControllerAdvice 相關,方法如下:

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    // <1> 掃描 @ControllerAdvice 註解的 Bean 們,生成對應的 ControllerAdviceBean 對象,並將進行排序
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    AnnotationAwareOrderComparator.sort(adviceBeans);

    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

    // <2> 遍歷 ControllerAdviceBean 數組
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        // <2.1> 掃描有 `@ModelAttribute` ,無 `@RequestMapping` 註解的方法,添加到 `modelAttributeAdviceCache` 屬性中
		// 該類方法用於在執行方法前修改 Model 對象
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        // <2.2> 掃描有 `@InitBinder` 註解的方法,添加到 `initBinderAdviceCache` 屬性中
		// 該類方法用於在執行方法前初始化數據綁定器
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        // <2.3> 如果是 RequestBodyAdvice 或 ResponseBodyAdvice 的子類,添加到 requestResponseBodyAdviceBeans 中
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }

    // <2.3> 將 requestResponseBodyAdviceBeans 添加到 this.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");
        }
    }
}
  1. 從 Spring 上下文掃描 @ControllerAdvice 註解的 Bean 們,生成對應的 ControllerAdviceBean 對象,並將進行排序,方法如下:

    public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
        return Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class))
                // 排除代理目標類,AOP 相關
                .filter(name -> !ScopedProxyUtils.isScopedTarget(name))
                // 包含 @ControllerAdvice 註解
                .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null)
                // 生成對應的 ControllerAdviceBean 對象
                .map(name -> new ControllerAdviceBean(name, context))
                .collect(Collectors.toList());
    }
    

    @ControllerAdvice 註解:用於 Controller 類的增強類,其中可定義多種增強的方法,例如 @ExceptionHandler 註解的方法用於處理器 Controller 拋出的異常

  2. 遍歷 1 中生成 ControllerAdviceBean 數組

    1. 掃描 @ModelAttribute @RequestMapping 註解的方法,添加到 modelAttributeAdviceCache 屬性中,該類方法用於在執行方法前修改 Model 對象
    2. 掃描 @InitBinder 註解的方法,添加到 initBinderAdviceCache 屬性中,該類方法用於在執行方法前初始化數據綁定器
    3. 如果是 RequestBodyAdvice 或 ResponseBodyAdvice 的子類,保存至 requestResponseBodyAdviceBeans 臨時變數中
  3. 2.3 的 requestResponseBodyAdviceBeans 保存至 requestResponseBodyAdvice 屬性中

1.2 getDefaultArgumentResolvers

getDefaultArgumentResolvers(),初始化默認的參數解析器,方法如下:

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;
}
  • 按順序添加了非常多的參數解析器對象

1.3 getDefaultInitBinderArgumentResolvers

getDefaultInitBinderArgumentResolvers(),初始化默認的參數綁定器,方法如下:

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

1.4 getDefaultReturnValueHandlers

getDefaultReturnValueHandlers(),初始化默認的返回值處理器,方法如下:

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;
}
  • 按順序添加了非常多的返回值處理器對象

supportsInternal 方法

實現 supportsInternal() 介面,方法如下:

@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
}

直接返回 true,也就是說處理器只要是 HandlerMethod 對象就可以

getLastModifiedInternal 方法

@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
    return -1;
}

直接返回 -1

handleInternal 方法

實現 handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) 方法,處理請求,執行處理器,方法如下:

@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, 
                                      HandlerMethod handlerMethod) throws Exception {
    ModelAndView mav;
    // <1> 校驗請求(HttpMethod 和 Session 的校驗)
    checkRequest(request);
    // <2> 調用 HandlerMethod 方法
    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) { // 同步相同 Session 的邏輯,默認情況false
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 獲取Session的鎖對象
            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...
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) { // 響應不包含'Cache-Control'頭
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }
    return mav;
}
  1. 調用父類 WebContentGenerator 的 checkRequest(ttpServletRequest request) 方法,校驗請求(HttpMethod 和 Session)是否合法

    protected final void checkRequest(HttpServletRequest request) throws ServletException {
        // Check whether we should support the request method.
        String method = request.getMethod();
        if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
            throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
        }
        // Check whether a session is required.
        if (this.requireSession && request.getSession(false) == null) {
            throw new HttpSessionRequiredException("Pre-existing session required but none found");
        }
    }
    

    在 AbstractHandlerMethodAdapter 的構造方法中,傳入 restrictDefaultSupportedMethods 參數為 false,表示不需要嚴格校驗 HttpMethod,這裡正常情況都會校驗通過

  2. 調用 invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) 方法,執行 HandlerMethod 處理器

    這裡會判斷 synchronizeOnSession 屬性,控制是否同步相同 Session 的邏輯,其中 WebUtils#getSessionMutex(session) 方法,獲得用來鎖的對象,方法如下:

    public static Object getSessionMutex(HttpSession session) {
        Assert.notNull(session, "Session must not be null");
        Object mutex = session.getAttribute(SESSION_MUTEX_ATTRIBUTE);
        if (mutex == null) {
            mutex = session;
        }
        return mutex;
    }
    

    當然,因為鎖是通過 synchronized 是 JVM 進程級,所以在分散式環境下,無法達到同步相同 Session 的功能

    默認情況下,synchronizeOnSessionfalse

【重點】invokeHandlerMethod方法

invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) 方法,執行 HandlerMethod 處理器,方法如下:

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, 
                                           HandlerMethod handlerMethod) throws Exception {
    // <1> 創建 ServletWebRequest 對象
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // <2> 創建 WebDataBinderFactory 對象
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        // <3> 創建 ModelFactory 對象
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // <4> 創建 ServletInvocableHandlerMethod 對象,並設置其相關屬性
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // <5> 創建 ModelAndViewContainer 對象,並初始其相關屬性
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // <6> 創建 AsyncWebRequest 非同步請求對象
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        // <7> 創建 WebAsyncManager 非同步請求管理器對象
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        // <8>
        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // <9> 執行調用
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        // <10>
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // <11> 獲得 ModelAndView 對象
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        // <12> 標記請求完成
        webRequest.requestCompleted();
    }
}

因為,Spring MVC 提供了大量的特性,所以 HandlerAdapter 又涉及許多組件。😈 我們主要先梳理好主流程,所以涉及的組件,還是先不詳細解析。我們的目的是,看到怎麼調用 HandlerMethod 方法的,即調用 Controller 的 @RequestMapping 註解的方法。

  1. 創建 ServletWebRequest 對象,包含了 request 請求和 response響應

  2. 調用 getDataBinderFactory(HandlerMethod handlerMethod) 方法,創建 WebDataBinderFactory 對象,有關於數據綁定,暫時忽略

  3. 調用 getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) 方法,創建 ModelFactory 對象,有關於往 Model 對象設置數據,暫時忽略

  4. 【核心】調用 createInvocableHandlerMethod(HandlerMethod handlerMethod) 方法,創建 ServletInvocableHandlerMethod 對象,然後設置其屬性。本文會對 ServletInvocableHandlerMethod 做簡單的解析。 詳細的解析在《HandlerAdapter 組件(二)之 ServletInvocableHandlerMethod》

  5. 創建 ModelAndViewContainer 對象,並初始其相關屬性

  6. 創建 AsyncWebRequest 非同步請求對象,暫時忽略

  7. 創建 WebAsyncManager 非同步請求管理器對象,暫時忽略

  8. 非同步處理,並髮結果相關,暫時忽略

  9. 【核心】調用 ServletInvocableHandlerMethodinvokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,執行處理器,方法如下:

    // ServletInvocableHandlerMethod.java
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, 
                                Object... providedArgs) throws Exception {
        // <1> 執行調用
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        // <2> 設置響應狀態碼
        setResponseStatus(webRequest);
    
        // <3> 設置 ModelAndViewContainer 為請求已處理,返回,和 @ResponseStatus 註解相關
        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }
        // <4> 設置 ModelAndViewContainer 為請求未處理
        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        try {
            // <5> 處理返回值
            this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }
    
    // InvocableHandlerMethod.java
    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
        // <y> 解析參數
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 執行調用
        return doInvoke(args);
    }
    
    // InvocableHandlerMethod.java
    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
        // <z1> 設置方法為可訪問
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            // <z2> 執行調用
            return getBridgedMethod().invoke(getBean(), args);
        } catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        } catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }
    

    可以大致過一下上面的執行邏輯,解析參數,通過反射執行方法,解析執行結果,詳細解析在後續的《HandlerAdapter 組件(二)之 ServletInvocableHandlerMethod》文檔中

  10. 非同步處理,並髮結果相關,暫時忽略

  11. 調用 getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) 方法,獲得 ModelAndView 對象,方法如下:

    @Nullable
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    
        modelFactory.updateModel(webRequest, mavContainer);
        // 情況一,如果 mavContainer 已處理,則返回「空」的 ModelAndView 對象。
        if (mavContainer.isRequestHandled()) {
            return null;
        }
        // 情況二,如果 mavContainer 未處理,則基於 `mavContainer` 生成 ModelAndView 對象
        ModelMap model = mavContainer.getModel();
        // 創建 ModelAndView 對象,並設置相關屬性
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        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;
    }
    
    • 情況一,如果 mavContainer 已處理,則返回「空」的 ModelAndView 對象,@ResponseBody 註解的結果處理則直接返回 null
    • 情況二,如果 mavContainer 未處理,則基於 mavContainer 生成 ModelAndView 對象

    在後續的文檔分析中會講到,注意這裡的 requestHandled 屬性,到時候再回過頭來理解😈

  12. 標記請求完成,暫時忽略

總結

Spring MVC 通過 HandlerMapping 組件會為請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors)。其中處理器的實現有多種,例如通過實現 Controller 介面、HttpRequestHandler 介面,或者使用 @RequestMapping 註解將方法作為一個處理器等。這就導致 Spring MVC 無法直接執行這個處理器,所以這裡需要一個處理器適配器,由它去執行處理器。

HandlerAdapter 處理器適配器對應的也有多種,那種適配器支援處理這種類型的處理器,則由該適配器去執行,如下:

  • HttpRequestHandlerAdapter:執行實現了 HttpRequestHandler 介面的處理器
  • SimpleControllerHandlerAdapter:執行實現了 Controller 介面的處理器
  • SimpleServletHandlerAdapter:執行實現了 Servlet 介面的處理器
  • RequestMappingHandlerAdapter:執行 HandlerMethod 類型的處理器,也就是通過 @RequestMapping 等註解標註的方法

這裡我們重點看 RequestMappingHandlerAdapter 對象,因為這種方式是目前使用最普遍的,其他類型的 HandlerAdapter 處理器適配器做了解即可

本文講述了 RequestMappingHandlerAdapter 處理執行器的整個流程,大致邏輯如下:

  1. 通過 ServletInvocableHandlerMethodHandlerMethod 處理器的封裝)對象去執行
  2. 需要通過 HandlerMethodArgumentResolver 對象進行參數解析
  3. 通過反射執行對應的 Method 方法對象
  4. 需要通過 HandlerMethodReturnValueHandler 對象對執行結果進行處理,設置到 response 響應中,生成對應的 ModelAndView 對象

上面涉及到的三個組件分別在後續的文檔中進行解析,先整體,後局部😈

參考文章:芋道源碼《精盡 Spring MVC 源碼分析》