SpringMVC原始碼分析系列(精簡)

hahaeee發表於2018-03-19

SpringMVC原始碼分析系列:

啟動過程

常見以web.xml配置方式

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <display-name>CtrTimeOut</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <context-param>
        <param-name>contextConfigLocation</param-name>
      	# spring的配置
        <param-value>classpath:config/spring/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>controller</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
          	# springmvc的配置
            <param-value>classpath:config/spring/spring-controller.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>controller</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
複製程式碼

ContextLoadListener建立WebApplicationContext作為spring的容器上下文

#org.springframework.web.context.ContextLoader
/**
 * 根據xml配置建立applicationContext
 */
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	...
	try {
		// Store context in local instance variable, to guarantee that
		// it is available on ServletContext shutdown.
		if (this.context == null) {//判空 (以註解方式配置時非空)
			this.context = createWebApplicationContext(servletContext);
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				...
				//讀取contextConfigLocation配置並refresh()
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
//將applicationContext設定到servletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
		...
		return this.context;
	}
}
複製程式碼

DispatcherServlet建立WebApplicationContext作為springmvc的上下文 並將ContextLoadListener建立的上下文設定為自身的parent

DispatcherServlet extends FrameworkServlet 
#org.springframework.web.servlet.FrameworkServlet
@Override
protected final void initServletBean() throws ServletException {
	...
	try {
		this.webApplicationContext = initWebApplicationContext();
		initFrameworkServlet();
	}
	...
}
protected WebApplicationContext initWebApplicationContext() {
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;
	...
	if (wac == null) {
		//建立applicationContext
		wac = createWebApplicationContext(rootContext);
	}
	...
	return wac;
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
	//XmlWebApplicationContext 
	Class<?> contextClass = getContextClass();
	...
	//建立applicationContext
	ConfigurableWebApplicationContext wac =
			(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	wac.setEnvironment(getEnvironment());
	//設定parent(ContextLoadListener中建立的applicationContext)
	wac.setParent(parent);
	//讀取contextConfigLocation配置
	wac.setConfigLocation(getContextConfigLocation());
	//refresh()
	configureAndRefreshWebApplicationContext(wac);
	return wac;
}
複製程式碼

springmvc的applicationContext會去讀取配置檔案 我們來看一個最簡單的配置檔案

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" 
      default-autowire="byName">
   #springmvc容器掃描路徑
   <context:component-scan base-package="com.iflytek.ossp.ctrtimeout.controller"></context:component-scan>
   #spring4新增的標籤 主要是新增了預設的HandleMappin,ViewResolver,HandleAdapter
   <mvc:annotation-driven />
</beans>
複製程式碼

springMvc配置解析

根據spring的自定義schema解析機制 我們找到 在下圖位置

http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
複製程式碼

mvc標籤解析定義

可以看到mvc所有的標籤解析器都定義在此

public class MvcNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
		registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
		registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
		registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
		registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
	}
}

複製程式碼

來看一下AnnotationDrivenBeanDefinitionParser解析器做了什麼

解析過程較為複雜 通過註釋我們可以得知以下物件將被裝載

annotation-driven

DispatcherServlet呼叫過程

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	...
	try {
		ModelAndView mv = null;
		Exception dispatchException = null;
		try {
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);
			//1.呼叫handlerMapping獲取handlerChain
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}
			// 2.獲取支援該handler解析的HandlerAdapter
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
			...
			// 3.使用HandlerAdapter完成handler處理
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			// 4.檢視處理(頁面渲染)
			applyDefaultViewName(request, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	...
}
複製程式碼

過程概括:

  1. 呼叫HandleMapping得到handler
  2. 呼叫HandleAdapter執行handle過程(引數解析 過程呼叫)
  3. 呼叫ViewResolver進行檢視解析
  4. 渲染檢視

springmvc流程
上述圖片來自網路

HandleMapping

定義: 請求路徑-處理過程對映管理

打個比方就是根據你的http請求的路徑得到可以處理的handler(你的Controller方法)

/**
 * Interface to be implemented by objects that define a mapping between
 * requests and handler objects.
 */
public interface HandlerMapping {
  //根據request獲取處理鏈
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

複製程式碼

以RequestMappingHandlerMapping為例 我們先看下他的繼承關係

RequestMappingHandlerMapping

可以看到有個InitlizingBean(spring的生命週期介面)我們就由他入手

#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
//1.
@Override
public void afterPropertiesSet() {
   if (this.useRegisteredSuffixPatternMatch) {
      this.fileExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
   }
   super.afterPropertiesSet();
}
//4.
@Override
protected boolean isHandler(Class<?> beanType) {
	return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
			(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
//6.
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = null;
	RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
	if (methodAnnotation != null) {
		//組裝對映資訊
		RequestCondition<?> methodCondition = getCustomMethodCondition(method);
		info = createRequestMappingInfo(methodAnnotation, methodCondition);
		RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
		if (typeAnnotation != null) {
			RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
			info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
		}
	}
	return info;
}
#org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
//2.
@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}
/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #isHandler(Class)
 * @see #getMappingForMethod(Method, Class)
 * @see #handlerMethodsInitialized(Map)
 */
 //3.
protected void initHandlerMethods() {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
	}
	//從容器中獲取所有object型別名
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
			getApplicationContext().getBeanNamesForType(Object.class));
	for (String beanName : beanNames) {
		//抽象,過濾(在RequestMappingHandlerMapping中根據Controller和RequestMapping註解過濾)
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
				isHandler(getApplicationContext().getType(beanName))){
			//探測類中定義的handler方法
			detectHandlerMethods(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}
//5.
protected void detectHandlerMethods(final Object handler) {
	Class<?> handlerType =
			(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
	final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
	final Class<?> userType = ClassUtils.getUserClass(handlerType);
	//得到符合條件的handler方法
	Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
		@Override
		public boolean matches(Method method) {
			//抽象,得到對映資訊(如RequestMappingInfo)
			T mapping = getMappingForMethod(method, userType);
			if (mapping != null) {
				mappings.put(method, mapping);
				return true;
			}
			else {
				return false;
			}
		}
	});
	//註冊handler方法
	for (Method method : methods) {
		registerHandlerMethod(handler, method, mappings.get(method));
	}
}
複製程式碼

上述程式碼就是HandlerMapping初始化對映關係的程式碼

過程概括:

  1. 獲取所有object子類
  2. 根據條件過濾出handle處理類
  3. 解析handle類中定義的處理方法
  4. 儲存解析得出的對映關係

來看一下getHandler(Request)的方法實現,看看DispatcherServlet是如何得到處理鏈的

#org.springframework.web.servlet.handler.AbstractHandlerMapping
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  //抽象,呼叫子類實現得到一個handler(可以是任一物件,需要通過HandleAdapter來解析)
  //RequestMappingInfoHandlerMapping中具體實現就是匹配請求路徑和RequestMapping註解
   Object handler = getHandlerInternal(request);
   ...
  //包裝handle成HandlerExecutionChain
   return getHandlerExecutionChain(handler, request);
}
複製程式碼

HandleAdapter

定義: 根據HandlerMapping.getHandler()得到的Handler資訊,對http請求引數解析並繫結

先看一下HandlerAdapter的介面定義

public interface HandlerAdapter {
   //判斷是否支援該handler型別的解析
   boolean supports(Object handler);
   //引數解析 並呼叫handler完成過程呼叫 
   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
   //用於處理http請求頭中的last-modified
   long getLastModified(HttpServletRequest request, Object handler);

}

複製程式碼

以RequestMappingHandlerAdapter為例來講,先看下繼承關係

RequestMappingHandlerAdapter

同樣看到了實現了InitializingBean介面 從這入手

#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
@Override
public void afterPropertiesSet() {
 // 1.初始化ControllerAdvice註解的物件
 initControllerAdviceCache();
// 2.裝載ArgumentResolver(預設+自定義)
 if (this.argumentResolvers == null) {
    List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
    //包裝成一個Composite物件
    this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
 }
 // 2.裝載InitBinderArgumentResolvers(預設+自定義)
 if (this.initBinderArgumentResolvers == null) {
    List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
    //包裝成一個Composite物件
    this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
 }
 // 3.裝載ReturnValueHandlers(預設+自定義)
 if (this.returnValueHandlers == null) {
    List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
    //包裝成一個Composite物件
    this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
 }
}
private void initControllerAdviceCache() {
	//從容器中獲取所有帶有ControllerAdvices註解的類名 幷包裝成ControllerAdviceBean
	List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
	OrderComparator.sort(beans);
	List<Object> responseBodyAdviceBeans = new ArrayList<Object>();
	for (ControllerAdviceBean bean : beans) {
		//篩選出帶有ModelAttribute且不帶RequestMapping註解的方法
		Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
		if (!attrMethods.isEmpty()) {
			//儲存到map中
			this.modelAttributeAdviceCache.put(bean, attrMethods);
		}
		//篩選出帶InitBinder註解的方法
		Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
		if (!binderMethods.isEmpty()) {
        	//儲存到map中
			this.initBinderAdviceCache.put(bean, binderMethods);
		}
		//如果該類同時實現了ResponseBodyAdvice介面 新增到結合中
		if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
			responseBodyAdviceBeans.add(bean);
		}
	}
	//儲存到全域性變數中
	if (!responseBodyAdviceBeans.isEmpty()) {
		this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
	}
}
複製程式碼

過程概括:

  1. 裝載帶有ControllerAdvices註解的物件
  2. 裝載ArgumentResolvers(預設+自定義)
  3. 裝載InitBinderArgumentResolvers(預設+自定義)
  4. 裝載ReturnValueHandlers(預設+自定義)

自定義擴充方式放後面說

以下為HandlerAdapter預設解析器

RequestHandlerAdapter

看一下HandlerMethodReturnValueHandler介面和HandlerMethodArgumentResolver介面

//引數解析器
public interface HandlerMethodArgumentResolver {
  //判斷是否支援該引數的解析(根據型別,註解等)
   boolean supportsParameter(MethodParameter parameter);
  //對引數進行解析 得到解析結果
   Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
//返回值解析器
public interface HandlerMethodReturnValueHandler {
  //判斷是否支援該返回值的解析(根據型別,註解等)
	boolean supportsReturnType(MethodParameter returnType);
  //對返回值進行解析
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

複製程式碼

上述即HandlerAdapter的初始化過程

DispatcherServlet呼叫HandlerAdapter過程

//1.呼叫support()方法判斷是否支援改handler的解析
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果是Get或Head請求 呼叫getLastModified()獲取上次更新時間 
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
   long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
   if (logger.isDebugEnabled()) {
      logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
   }
  //如果小於瀏覽器快取更新時間 則直接返回 瀏覽器使用本地快取
   if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
      return;
   }
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
複製程式碼

DisPatcherServlet呼叫HandlerAdapter分為三步:

  1. 呼叫support()方法判斷是否支援改handler的解析
#org.springframework.web.servlet.DispatcherServlet
//在doDispatch()方法中呼叫了getHandlerAdapter(Object)方法來得到一個HandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    //呼叫HandlerAdapter.support()方法 判斷是否支援該handler物件的解析
   for (HandlerAdapter ha : this.handlerAdapters) {
        ...
      if (ha.supports(handler)) {
         return ha;
      }
   }
  ...
}
#org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter
@Override
public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
	return true;
}
複製程式碼
  1. 如果是Get或Head請求 呼叫getLastModified()獲取上次更新時間

如果小於瀏覽器快取更新時間 則直接返回 瀏覽器使用本地快取

#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
   return -1;
}
複製程式碼
  1. 呼叫handler()方法完成過程呼叫(引數解析 返回值解析)
#org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
   return handleInternal(request, response, (HandlerMethod) handler);
}

#org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//對http協議快取方面的請求頭的處理(expire,cache-control)
	if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
		// Always prevent caching in case of session attribute management.
		checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
	}
	else {
		// Uses configured default cacheSeconds setting.
		checkAndPrepare(request, response, true);
	}
	// Execute invokeHandlerMethod in synchronized block if required.
	if (this.synchronizeOnSession) {//是否使用session鎖
		HttpSession session = request.getSession(false);
		if (session != null) {
          	//得到互斥量
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {//執行過程呼叫
				return invokeHandleMethod(request, response, handlerMethod);
			}
		}
	}
	//執行過程呼叫
	return invokeHandleMethod(request, response, handlerMethod);
}
//根據HandlerMethod解析引數 並完成過程呼叫得到一個ModelAndView
private ModelAndView invokeHandleMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	//使用initBinderAdviceCache對@initBinder進行處理
	WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    // 使用modelAttributeAdviceCache對@ModelAttribute進行處理
	ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
	ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
	ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
	modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
	//對非同步的處理 暫時不管 TODO後面再分析
  	...
  	//1 完成過程呼叫
	requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
	if (asyncManager.isConcurrentHandlingStarted()) {
		return null;
	}
	//2 包裝ModelAndView
	return getModelAndView(mavContainer, modelFactory, webRequest);
}
複製程式碼

handle()過程總結:

  1. 執行過程呼叫requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
#org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest,
                    ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
  // 1.1 引數解析 並完成過程呼叫
  Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  setResponseStatus(webRequest);
  ...
    try {
      //1.2 使用returnValueHandlers對返回結果進行處理 講結果塞到mavContainer中 過程類似引數解析
      this.returnValueHandlers.handleReturnValue(
        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
}
#org.springframework.web.method.support.InvocableHandlerMethod
        public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
                                       Object... providedArgs) throws Exception {
      //1.1.1 引數解析並得到繫結的結果
      Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
      ...
        //1.1.2 反射完成過程呼叫 
        Object returnValue = doInvoke(args);
      ...
        return returnValue;
    }
    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
                                             Object... providedArgs) throws Exception {
      //引數資訊
      MethodParameter[] parameters = getMethodParameters();
      Object[] args = new Object[parameters.length];
      for (int i = 0; i < parameters.length; i++) {
        //呼叫HandlerMethodArgumentResolver#supportsParameter判斷是否支援
        if (this.argumentResolvers.supportsParameter(parameter)) {
          try {
            //呼叫HandlerMethodArgumentResolver#resolveArgument進行解析
            args[i] = this.argumentResolvers.resolveArgument(
              parameter, mavContainer, request, this.dataBinderFactory);
            continue;
          }
          ...
        }
        ...
      }
      return args;
    }
複製程式碼
  1. 包裝ModelAndView getModelAndView(mavContainer, modelFactory, webRequest);
//從mavContainer取出結果 包裝成ModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                                     ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
  modelFactory.updateModel(webRequest, mavContainer);
  if (mavContainer.isRequestHandled()) {
    return null;
  }
  ModelMap model = mavContainer.getModel();
  ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
  if (!mavContainer.isViewReference()) {
    mav.setView((View) mavContainer.getView());
  }
  //如果是redirect請求
  if (model instanceof RedirectAttributes) {
    Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
  }
  return mav;
}
複製程式碼

到此 HandlerAdapter的呼叫過程算分析完了

springMVC思維導圖

相關文章