我的架構夢:(九)SpringMVC源碼分析

一、前端控制器 DispatcherServlet 繼承結構

我們在上一篇知道了用戶發送請求至前端控制器DispatcherServlet。所以我們先來分析下DispatcherServlet的繼承結構關係。如下圖:
在這裏插入圖片描述

二、 重要時機點分析

1、handle方法中打斷點

@Controller
@RequestMapping("/riemann")
public class RiemannController  {

	@RequestMapping("/handle")
	public String handle(String name, Map<String,Object> model) {
		System.out.println("handler業務邏輯處理中....");
		Date date = new Date();
		model.put("date", date);
		return "success";
	}

}

2、觀察調用棧

在這裏插入圖片描述

doDispathch方法中的1064行代碼完成handler方法的調用

3、⻚面渲染時機(打斷點並觀察調用棧)

在這裏插入圖片描述

三、SpringMVC處理請求的流程

org.springframework.web.servlet.DispatcherServlet#doDispatch方法的執行過程,其中步驟 2、3、4、5是核心步驟

1、調用getHandler()獲取到能夠處理當前請求的執行鏈 HandlerExecutionChain(Handler+攔截器)
2、調用getHandlerAdapter();獲取能夠執行1中Handler的適配器
3、適配器調用Handler執行ha.handle(總會返回一個ModelAndView對象)
4、調用processDispatchResult()方法完成視圖渲染跳轉

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
			// 1.檢查是否是文件上傳的請求
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// Determine handler for the current request.
			/**
			 * 2.取得處理當前請求的 Controller,這裏也稱爲 Handler,即處理器
			 *   這裏並不是直接返回 Controller,而是返回 HandlerExecutionChain 請求處理鏈對象
			 *   該對象封裝了 Handler 和 Interceptor
			 */
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				// 如果 handler 爲空,則返回404
				noHandlerFound(processedRequest, response);
				return;
			}

			// Determine handler adapter for the current request.
			// 3.獲取處理請求的處理器適配器 HandlerAdapter
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// Process last-modified header, if supported by the handler.
			// 處理 last-modified 請求頭
			String method = request.getMethod();
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}

			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// Actually invoke the handler.
			// 4.實際處理器處理請求,返回結果視圖對象。
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}

			// 結果視圖對象的處理
			applyDefaultViewName(processedRequest, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			// As of 4.3, we're processing Errors thrown from handler methods as well,
			// making them available for @ExceptionHandler methods and other scenarios.
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
		// 該方法完成視圖渲染跳轉
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
	catch (Exception ex) {
		// 最終會調用 HandlerInterceptor 的 afterCompletion 方法
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	}
	catch (Throwable err) {
		triggerAfterCompletion(processedRequest, response, mappedHandler,
				new NestedServletException("Handler processing failed", err));
	}
	finally {
		if (asyncManager.isConcurrentHandlingStarted()) {
			// Instead of postHandle and afterCompletion
			if (mappedHandler != null) {
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		}
		else {
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}

四、核心步驟getHandler方法剖析

遍歷兩個HandlerMapping,試圖獲取能夠處理當前請求的執行鏈

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

五、核心步驟getHandlerAdapter方法剖析

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 [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

六、核心步驟ha.handle方法剖析

在這裏插入圖片描述

繼續跟到 AbstractHandlerMethodAdapter#handle

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	// 判斷當前是否需要支持在同一個session中只能線性的處理請求
	if (this.synchronizeOnSession) {
		// 獲取當前請求的session對象
		HttpSession session = request.getSession(false);
		if (session != null) {
			// 爲當前session生成一個唯一的可以用於鎖定的key
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				// 對HandlerMethod進行參數等的適配處理,並調用目標handler
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			// 如果當前不存在session,則直接對HandlerMethod進行適配
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// No synchronization on session demanded at all...
		// 如果當前不需要對session進行同步處理,則直接對HandlerMethod進行適配
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}

	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}

	return mav;
}

在這裏插入圖片描述

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		// 獲取容器中全局配置的InitBinder和當前HandlerMethod所對應的Controller中配置的InitBinder,用於進行參數的綁定。
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		// 獲取容器中全局配置的ModelAttribute和當前HandlerMethod所對應的Controller中配置的ModelAttribute,這些配置的方式將會在目標方法調用之前進行
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		// 將handlerMethod封裝爲一個ServletInvocableHandlerMethod對象
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			// 設置當前容器中配置的所有ArgumentResolver
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			// 設置當前容器中配置的所有ReturnValueHandler
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		// 將前面創建的WebDataBinderFactory設置到ServletInvocableHandlerMethod中
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		/**
		 * 這裏initModel方法主要作用是調用前面獲取到的@ModelAttribute標註的方法
		 * 從而達到ModelAttribute標註的方法能夠在目標Handler調用之前調用的目的
		 */
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
		
		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

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

		// 對請求參數進行處理,調用目標HandleMethod,並且將返回值封裝爲一個ModelAndViewd對象
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		/**
		 * 對封裝的ModelAndView進行處理,主要是判斷當前的請求是否進行了重定向,
		 * 如果進行了重定向,還會判斷是否需要將FlashAttributes封裝到新的請求中。
		 */
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

在這裏插入圖片描述

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

	// 對目標handler的參數進行處理,並且調用目標handler
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	// 設置相關的返回狀態
	setResponseStatus(webRequest);

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

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 將request中的參數轉換爲當前handler的參數形式
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	// 主要是結合處理後的參數,使用反射對目標方法進行調用
	return doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

	// 獲取當前handler所聲明的所有參數,主要包括參數名、參數類型、參數位置、所標註的註解等等屬性
	MethodParameter[] parameters = getMethodParameters();
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;
	}

	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		/**
		 * providedArgs是調用方提供的參數,這裏主要是判斷這些參數中是否有當前類型,如果有,則直接使用調用方提供的參數,對於請求處理而言,默認情況下,
		 * 調用方提供的參數都是長度爲0的數組
		 */
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		/**
		 * 如果在調用方提供的參數中不能找到當前類型的參數值,則遍歷Spring容器中所有的ArgumentResolver,
		 * 判斷哪種類型的Resolver支持對當前參數的解析,這裏的判斷方式比較簡單,比如
		 * RequestMethodArgumentResolver就是判斷當前參數是否使用@RequestParam註解進行了標註。
		 */
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			/**
			 * 如果能夠找到對當前參數進行處理的ArgumentResolver,則調用其resolveArgument方法
			 * 從request中獲取對應的參數值,並且進行轉換。
			 */
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		catch (Exception ex) {
			// Leave stack trace for later, exception may actually be resolved and handled...
			if (logger.isDebugEnabled()) {
				String exMsg = ex.getMessage();
				if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
					logger.debug(formatArgumentError(parameter, exMsg));
				}
			}
			throw ex;
		}
	}
	return args;
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

	HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
	if (resolver == null) {
		throw new IllegalArgumentException("Unsupported parameter type [" +
				parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
	}
	return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

	NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
	MethodParameter nestedParameter = parameter.nestedIfOptional();

	Object resolvedName = resolveStringValue(namedValueInfo.name);
	if (resolvedName == null) {
		throw new IllegalArgumentException(
				"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
	}

	Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
	if (arg == null) {
		if (namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}
		else if (namedValueInfo.required && !nestedParameter.isOptional()) {
			handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
		}
		arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
	}
	else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
		arg = resolveStringValue(namedValueInfo.defaultValue);
	}

	if (binderFactory != null) {
		WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
		try {
			arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
		}
		catch (ConversionNotSupportedException ex) {
			throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
					namedValueInfo.name, parameter, ex.getCause());
		}
		catch (TypeMismatchException ex) {
			throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
					namedValueInfo.name, parameter, ex.getCause());

		}
	}

	handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

	return arg;
}
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
	HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);

	if (servletRequest != null) {
		Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
		if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
			return mpArg;
		}
	}

	Object arg = null;
	MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
	if (multipartRequest != null) {
		List<MultipartFile> files = multipartRequest.getFiles(name);
		if (!files.isEmpty()) {
			arg = (files.size() == 1 ? files.get(0) : files);
		}
	}
	if (arg == null) {
		String[] paramValues = request.getParameterValues(name);
		if (paramValues != null) {
			arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
		}
	}
	return arg;
}

在這裏插入圖片描述

七、 核心步驟processDispatchResult方法剖析

1、render方法完成渲染

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

2、視圖解析器解析出View視圖對象

在這裏插入圖片描述
在這裏插入圖片描述
3、在解析出View視圖對象的過程中會判斷是否重定向、是否轉發等,不同的情況封裝的是不同的 View實現

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

4、解析出View視圖對象的過程中,要將邏輯視圖名解析爲物理視圖名

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
	Class<?> viewClass = getViewClass();
	Assert.state(viewClass != null, "No view class");

	AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
	// 邏輯視圖名轉換爲物理視圖名
	view.setUrl(getPrefix() + viewName + getSuffix());

	String contentType = getContentType();
	if (contentType != null) {
		view.setContentType(contentType);
	}

	view.setRequestContextAttribute(getRequestContextAttribute());
	view.setAttributesMap(getAttributesMap());

	Boolean exposePathVariables = getExposePathVariables();
	if (exposePathVariables != null) {
		view.setExposePathVariables(exposePathVariables);
	}
	Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
	if (exposeContextBeansAsAttributes != null) {
		view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
	}
	String[] exposedContextBeanNames = getExposedContextBeanNames();
	if (exposedContextBeanNames != null) {
		view.setExposedContextBeanNames(exposedContextBeanNames);
	}

	return view;
}

5、封裝View視圖對象之後,調用了view對象的render方法

在這裏插入圖片描述

6、渲染數據

在這裏插入圖片描述

7、把modelMap中的數據暴露到request域中,這也是爲什麼後臺model.add之後在jsp中可以從請求域取出來的根本原因。

繼續跟進去

@Override
protected void renderMergedOutputModel(
		Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

	// Expose the model object as request attributes.
	exposeModelAsRequestAttributes(model, request);

	// Expose helpers as request attributes, if any.
	exposeHelpers(request);

	// Determine the path for the request dispatcher.
	String dispatcherPath = prepareForRendering(request, response);

	// Obtain a RequestDispatcher for the target resource (typically a JSP).
	RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
	if (rd == null) {
		throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
				"]: Check that the corresponding file exists within your web application archive!");
	}

	// If already included or response already committed, perform include, else forward.
	if (useInclude(request, response)) {
		response.setContentType(getContentType());
		if (logger.isDebugEnabled()) {
			logger.debug("Including [" + getUrl() + "]");
		}
		rd.include(request, response);
	}

	else {
		// Note: The forwarded resource is supposed to determine the content type itself.
		if (logger.isDebugEnabled()) {
			logger.debug("Forwarding to [" + getUrl() + "]");
		}
		rd.forward(request, response);
	}
}

8、將數據設置到請求域中

protected void exposeModelAsRequestAttributes(Map<String, Object> model,
			HttpServletRequest request) throws Exception {

	model.forEach((name, value) -> {
		if (value != null) {
			request.setAttribute(name, value);
		}
		else {
			request.removeAttribute(name);
		}
	});
}

八、SpringMVC九大組件初始化

1、在DispatcherServlet中定義了九個屬性,每一個屬性都對應一種組件

/** MultipartResolver used by this servlet. */
/** 多部件解析器 */
@Nullable
private MultipartResolver multipartResolver;

/** LocaleResolver used by this servlet. */
/** 區域化、國際化解析器 */
@Nullable
private LocaleResolver localeResolver;

/** ThemeResolver used by this servlet. */
/** 主題解析器 */
@Nullable
private ThemeResolver themeResolver;

/** List of HandlerMappings used by this servlet. */
/** 處理器映射器組件 */
@Nullable
private List<HandlerMapping> handlerMappings;

/** List of HandlerAdapters used by this servlet. */
/** 處理器適配器組件 */
@Nullable
private List<HandlerAdapter> handlerAdapters;

/** List of HandlerExceptionResolvers used by this servlet. */
/** 異常解析器 */
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;

/** RequestToViewNameTranslator used by this servlet. */
/** 默認視圖名轉換器組件 */
@Nullable
private RequestToViewNameTranslator viewNameTranslator;

/** FlashMapManager used by this servlet. */
/** flash屬性管理組件 */
@Nullable
private FlashMapManager flashMapManager;

/** List of ViewResolvers used by this servlet. */
/** 視圖解析器 */
@Nullable
private List<ViewResolver> viewResolvers;

九大組件都是定義了接口,接口其實就是定義了該組件的規範,比如ViewResolverHandlerAdapter 等都是接口。

2、九大組件的初始化時機

DispatcherServlet中的onRefresh(),該方法中初始化了九大組件:

在這裏插入圖片描述

initStrategies方法

在這裏插入圖片描述

3、觀察其中的一個組件initHandlerMappings(context)

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 找到所有的 HandlerMapping
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			// 否則在ioc中按照固定名稱去找
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	if (this.handlerMappings == null) {
		// 最後還爲空的話則按照默認策略生成
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

4、如果按照類型和按照固定id從ioc容器中找不到對應組件,則會按照默認策略進行註冊初始化,默 認策略在DispatcherServlet.properties文件中配置

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
	String key = strategyInterface.getName();
	String value = defaultStrategies.getProperty(key); // DispatcherServlet.properties
	if (value != null) {
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		List<T> strategies = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			try {
				Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
				Object strategy = createDefaultStrategy(context, clazz);
				strategies.add((T) strategy);
			}
			catch (ClassNotFoundException ex) {
				throw new BeanInitializationException(
						"Could not find DispatcherServlet's default strategy class [" + className +
						"] for interface [" + key + "]", ex);
			}
			catch (LinkageError err) {
				throw new BeanInitializationException(
						"Unresolvable class definition for DispatcherServlet's default strategy class [" +
						className + "] for interface [" + key + "]", err);
			}
		}
		return strategies;
	}
	else {
		return new LinkedList<>();
	}
}

DispatcherServlet.properties
在這裏插入圖片描述

5、注意:多部件解析器的初始化必須按照id註冊對象(multipartResolver)

在這裏插入圖片描述


OK,SpringMVC源碼分析告一段落了。後面會對SpringBoot源碼進行分析,敬請期待。

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