精盡Spring MVC原始碼分析 - HandlerAdapter 元件(一)之 HandlerAdapter

月圓吖發表於2020-12-17

該系列文件是本人在學習 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 介面的體系結構如下:

精盡Spring MVC原始碼分析 - HandlerAdapter 元件(一)之 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 原始碼分析》

相關文章