SpringMVC源碼分析-DispatcherServlet-init方法分析

上一篇:SpringMVC源碼分析-DispatcherServlet實例化幹了些什麼html

先吐槽一下。。。寫了兩小時的博客忽然被俺家小屁孩按了刷新,東西不見了,建議OSCHINA可以自動定時保存啊。讓我先安靜一下。。。。java

由於前面在閱讀Tomcat源碼的時候時序圖中沒有畫調用Servlet init方法的步驟,在這裏補充一下文字說明。 Tomcat調用順序:web

StandardWrapperValve.invoke()
StandardWrapper.allocate()
StandardWrapper.loadServlet()
DispatcherServet(實際是父類的).init()

經過上面的調用過程最終到達了DispatcherServlet的init(),由Spring的DefaultListableBeanFactory去完成DispatcherServlet.properties中配置類的初始化工做spring

時序圖

說明

圖中最核心的方法就是DispatcherServlet.initStrategies(),見源代碼json

protected void initStrategies(ApplicationContext context) {
		/**
		 * 這裏的每個方法都很是的重要,他們都遵循一個邏輯:若是在容器中沒有拿到對應類型的對象,則使用DispatcherServlet.properties當中配置
		 * 的值,使用Spring容器建立一個新的Bean(注意是prototype,因此在singletonObjebs中沒法找到它們。。。爲何是prototype喃,說實話我也不
		 * 是很肯定,可能由於Servlet可能會有多個吧。。。但想一想又以爲很牽強由於DispatcherServlet只有一個對象,並且又是在init方法初始化的,
		 * init方法只會被調用一次,不管處理多少請求,它們都會是線程共享的啊。。。也許Spring並不想在singletonObjects當中存放它才設置爲prototype
		 * 的吧
		 *
		 * 由於是用Spring進行實例化的,因此可使用Spring提供的擴展點干預這些默認的類的行爲,好比能夠將protyotype設置爲singleton,能夠修改它們
		 * 一些屬性的默認值等等
		 */
		//文件上傳相關
		initMultipartResolver(context);
		//國際化 先從容器中拿名爲localResolve的LocaleResolver.class對象,若是沒有則使用默認的AcceptHeaderLocaleResolver(Spring-createBean)
		initLocaleResolver(context);
		//主題樣式 先從容器中拿名爲themeResolver的ThemeResolver.class對象,若是沒有則使用默認的FixedThemeResolver(Spring-createBean)
		initThemeResolver(context);
		/**--------------很是重要-------------
		 * 初始化請求映射規則
		 * 先從容器中拿名爲HandlerMapping.class對象列表,若是沒有則使用默認的如下三個配置項
		 * RequestMappingHandlerMapping:這是最重要的,是一般咱們在Controller當中配置的RequestMapping的映射處理類
		 * BeanNameUrlHandlerMapping:是咱們的Controller實現了Controller接口,而後用@Component("beanName"),訪問時url:localhost:port/contextpath/beanName
		 * RouterFunctionMapping:經過HttpRequestHandler實現Controller,具體細節不清楚,歷來沒見過更沒用過
		 */
		initHandlerMappings(context);
		/**--------------很是重要-------------
		 * Request\Response的重要處理,好比入參與出參的一樣格式化
		 * 先從容器中拿名爲HandlerAdapter.class對象列表,若是沒有則使用默認的如下四個配置項:
		 * HttpRequestHandlerAdapter:
		 * SimpleControllerHandlerAdapter:
		 * RequestMappingHandlerAdapter:
		 * HandlerFunctionAdapter:
		 */
		initHandlerAdapters(context);
		/**
		 * 異常 先從容器中拿HandlerExceptionResolver.class對象列表,若是沒有則使用默認的如下三個配置項
		 * ExceptionHandlerExceptionResolver:
		 * ResponseStatusExceptionResolver:
		 * DefaultHandlerExceptionResolver:
		 */
		initHandlerExceptionResolvers(context);
		//請求到視圖的轉換 從容器中拿名爲viewNameTranslator的RequestToViewNameTranslator,若是沒有則使用默認的DefaultRequestToViewNameTranslator
		initRequestToViewNameTranslator(context);
		//視圖轉換器 從容器中拿ViewResolver對象列表,若是沒有則使用默認的InternalResourceViewResolver
		initViewResolvers(context);
		//重定向視圖管理器 從容器中拿名爲flashMapManager的FlashMapManager,若是沒有則使用默認的SessionFlashMapManager
		initFlashMapManager(context);
	}

源碼中已經比較詳細的寫了每一個方法的註釋,就再也不贅述了。緩存

概要說明

這裏面的每一個方法固然都很重要,可是結合平常開發來分析,initHandlerMappings,initHandlerAdapters,initViewResolvers它們三個是解決請求映射、參數解析-綁定-格式化、視圖渲染等功能,因此重點拿initHandlerMappings,initHandlerAdapters方法來分析。這兩個方法底層邏輯都是先從Spring中找有麼有對應的Bean,若是沒有則使用DispatcherServlet.properties中配置的Bean,由Spring完成他們的實例化。因爲時間、篇幅等緣由就不分析裏面的每個類的實例化和功能,這三種分別拿一個比較典型的類進行分析。app

initHandlerMappings

使用RequestMappingHandlerMapping做爲典型進行講解它的實例化,不包含它的處理請求的部分源碼分析

此類乾的重要事情就是收集Controller當中的符合規則的HandlerMethod,在處理請求的時候,使用請求路徑和這些HandlerMethod進行匹配,找最優匹配進行處理ui

雖然我選擇了這個類作講解,可是從哪裏入手看喃?既然這個類是被Spring實例化的,那麼它確定實現什麼接口口、繼承了什麼類或者使用了什麼Spring的特殊註解,拿出它的類結構圖看看this

能夠看到實現了InitializingBean接口,Spring在實例化對象快結束的時候會調用實現類的afterPropertiesSet()方法。

那就看看這個方法

public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());
		//這裏面就是收集Controller的映射關係
		super.afterPropertiesSet();
	}

把斷點打到這裏一步步的往下走,最終能夠找到關鍵代碼,這個過程就省略了,我直接把調用棧貼出來,已經對比較重要方法作註釋

RequestMappingHandlerMapping.afterPropertiesSet()
AbstractHandlerMethodMapping.afterPropertiesSet()
AbstractHandlerMethodMapping.initHandlerMethods()
AbstractHandlerMethodMapping.processCandidateBean()
AbstractHandlerMethodMapping.detectHandlerMethods()
RequestMappingHandlerMapping.getMappingForMethod()

AbstractHandlerMethodMapping.initHandlerMethods()

protected void initHandlerMethods() {
		//循環Spring Context當中全部的Bean,看是否知足被@Controller或者@RequestMapping修飾的條件,若是知足則進行映射關係收集
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		/**
		 * 開啓鎖,返回一個只可讀的請求路徑與HandlerMethod的映射Map
		 */
		handlerMethodsInitialized(getHandlerMethods());
	}

AbstractHandlerMethodMapping.processCandidateBean()

protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		//isHandler:看BeanType是否@Controller或者@RequestMapping修飾,若是知足條件,才進行下面的映射關係收集邏輯
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

AbstractHandlerMethodMapping.detectHandlerMethods()

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			/**
			 * 從這裏開始有兩層調用都用到了lambda表達式,看源碼時須要注意,在DEBUG時,會跳回到上一層的lambda表達式對應的代碼段執行
			 *
			 * selectMethods裏面是在從緩存(建立Bean的時候由於其餘功能已經解析了一遍類的Class)中拿類的全部Method,而後
			 * 調用getMappingForMethod()方法將Controller符合條件的method與指望請求路創建關係以備請求時作匹配使用,並將匹配到結果放入methods集合
			 * 當中
			 */
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			/**
			 * 註冊HandlerMethod,只有註冊以後,在後面才能從registry當中拿到一個只讀的Mapping
			 */
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

RequestMappingHandlerMapping.getMappingForMethod()

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		/**
		 * 從Method上面拿到請求路徑
		 */
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			/**
			 * 從Type上面拿到請求路徑
			 */
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				/**
				 * 進行組合:好比method上面是"/user/{age}",type上面是"/my",則組合後的結果就是"/my/user/{age}"
				 */
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).build().combine(info);
			}
		}
		return info;
	}

這裏有必要把RequestMappingInfo單獨解釋一下,RequestMappingInfo對應@RequestMapping,裏面的屬性在init的時候也會根據@RequestMapping的參數值對應設置

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
​
    @Nullable
    private final String name;
    //Patterns對應url,就是RequestMapping註解value中的配置
    private final PatternsRequestCondition patternsCondition;
    //Methods對應 http method,如GET,POST,PUT,DELETE等
    private final RequestMethodsRequestCondition methodsCondition;
    //params對應http request parameter
    private final ParamsRequestCondition paramsCondition;
    //headers 對應http request 的請求頭
    private final HeadersRequestCondition headersCondition;
    //consumes對應request的提交內容類型content type,如application/json, text/html
    private final ConsumesRequestCondition consumesCondition;
    //produces指定返回的內容類型的content type,僅當request請求頭中的(Accept)類型中包含該指定類型才返回
    private final ProducesRequestCondition producesCondition;
    //若是以上都還不能達到你過濾請求的目的,還能夠自定義
    private final RequestConditionHolder customConditionHolder;
    
    //-------------省略不少代碼------------------
    /**
     * 請求匹配到多個RequestMapping,則須要排序,選擇最優的HandlerMethod進行處理
     */
    public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
        int result;
        // Automatic vs explicit HTTP HEAD mapping
        if (HttpMethod.HEAD.matches(request.getMethod())) {
            result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
            if (result != 0) {
                return result;
            }
        }
        result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.producesCondition.compareTo(other.getProducesCondition(), request);
        if (result != 0) {
            return result;
        }
        // Implicit (no method) vs explicit HTTP method mappings
        result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
        if (result != 0) {
            return result;
        }
        return 0;
    }
​
}

到此Spring已經使用RequestMappingHandlerMapping爲使用@Controller和@RequestMapping註解的Controller類收集好了全部的請求映射,等待處理請求。

initHandlerAdapters

使用RequestMappingHandlerAdapter做爲典型進行講解它的實例化,不包含它的處理請求的部分

分析它的套路和RequestMappingHandlerMapping同樣,上來就看afterPropertiesSet()方法

public void afterPropertiesSet() {
		/**
		 * 找到Spring容器中被@ControllerAdvice和@RestControllerAdvice修飾的Bean,並添加到一個List當中
		 * @ControllerAdvice是一個Controller加強器,在項目中曾經被用來作異常統一處理
		 * 這兩個註解能夠結合@ExceptionHandler, @InitBinder, @ModelAttribute三個註解使用將方法做用到全局上面
		 * 詳細使用參考官網:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html
		 */
		initControllerAdviceCache();
		/**
		 * ----------------很是重要----------------------
		 * argumentResolvers:參數解析器
		 * initBinderArgumentResolvers:初始化參數綁定解析器
		 * returnValueHandlers:Controller.method invoke以後返回值解析器
		 *
		 * SpringMVC都默認提供了一大批各類各樣的解析器,它們共同組成了SpringMVC的強大功能
		 *
		 */
		if (this.argumentResolvers == null) {
			/**
			 * 獲取默認的參數解析器,這些解析器是寫死在代碼中的
			 */
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			/**
			 * 取默認的InitBinder參數解析器,這些解析器是寫死在代碼中的
			 */
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			/**
			 * 取默認的返回值解析器,這些解析器是寫死在代碼中的
			 */
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

此方法很簡單,就是實例化默認的各類解析器,在此以前將@ControllerAdvice修飾的Bean找出來放在一個集合當中

關於@ControllerAdvice能夠在官網文檔中去出看或者百度

隨便找一個Revolver看看它的功能

RequestParamMethodArgumentResolver負責識別@RequestParam的參數,將其中的name和真正請求當中的Key進行匹配

PathVariableMethodArgumentResolver負責識別rest風格中具體位置中的參數值

Spring命名確實也很規範,經過看名稱幾乎就能知道它是幹什麼的,因此開發當中命名很重要,不要嫌棄名稱太長,寫起來太麻煩。

相關文章
相關標籤/搜索