深入Spring源碼系列----SpringMVC如何根據URL來選擇調用方法

SpringMVC以及Spring中的初始化

tomcat調用Service文件下javax.servlet.ServletContainerInitializer文件中配置的類的全限域名類(org.springframework.web.Spring.SpringServletContainerInitializer),然後通過循環遍歷調用實現@HandlersTypes註解中接口的類中的onStartup方法,從而將Listener對象和DispatcherServlet創建好,在創建DispatchServlet過程中,會創建一個關於DispathServlet上下文環境(WebApplicationContext),由於tomcat啓動的時候需要遵循tomcat規範,所以tomcat啓動的時候會調用到Listener中的contextInitialized方法,在這個方法中就會調用到Spring的核心refresh方法,tomcat啓動的時候會調用DispatcherServlet中的init()方法,在init方法中就會調用到refresh方法。DispatcherServlet調用玩Spring的refresh方法後會調用它本身的onRefresh方法(具體在initWebApplicationContext這個方法中調用到的),onRefresh這個方法就會完成SpringMVC中一系列容器的初始化工作。其中就會觸發實現了RequestMappinghandlerMapping的實例化,由於RequestMappingHandlerMapping擴展至AbstractHandlerMethModMapping這個抽象類,這個抽象類實現了InitializingBean接口,Spring在實例化Bean的過程中就會在調用到這個接口當中的afterPropertiesSet()方法(具體在AbstractAutowiredCapableBeanFactory中的invokerInitMehods()方法中調用到)。在afterPropertieSet方法中就會建立起url和類以及方法的映射關係。在AbstractHandlerMethodMapping中的afterPropertiesSet主要做建立以下的映射關係:
1.將方法上@RequestMapping註解進行封裝,封裝成RequestMappingInfo對象,並把類上的@RequestMapping註解合併到方法上
2.主要是在registerHandlerMethod方法中建立起URL到方法的映射,主要就是對以下重要的map賦值:
(1)對mappingLookup添加值,既建立起RequestMappingInfo對應和HandlerMethod對象的映射關係
(2)對urlLookup集合添加值,既建立起URL(字符串)和RequestMappingInfo的映射關係
(3)對corsLooup集合添加值,既建立起HandlerMethod和corsConfiguration(@CrossOrigin註解對象的封裝,主要是由於跨域問題)對象的映射關係
(4)對registry集合添加值,既建立起RequestMappingInfo對象和MappingRegistration對象(對RequestMappingInfo、HandlerMethod,URL集合(可能有多個URL可以訪問到方法),方法的全限域名的封裝類)的映射關係
至此HandlerMapping初始化完成。

		public void afterPropertiesSet() {
		initHandlerMethods();
	}
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				//在這個方法中就會建立起URL和方法的映射關係
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
	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);
			}
		}
		//如果類上面有@Controller註解或者@RequestMapping註解
		if (beanType != null && isHandler(beanType)) {
			//建立uri和method的映射關係
			detectHandlerMethods(beanName);
		}
	}
	protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);

			//獲取方法對象和方法上面的@RequestMapping註解屬性封裝對象的映射關係,這裏面的T對象就是RequestMappingInfo對象
			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));
			}
			methods.forEach((method, mapping) -> {
				//選擇可以反射的方法
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				//建立uri和方法的各種映射關係,反正一條,根據uri要能夠找到method對象  ,建立起方法和RequestMappingInfo之間的映射關係
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
		public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				//創建HandlerMethod對象,其實就是對方法的封裝,
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				//檢驗是否唯一,檢查方法和RequestMappingInfo映射關係的唯一性
				assertUniqueMethodMapping(handlerMethod, mapping);
				//建立uri對象和handlerMethod的映射關係
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					//建立url和RequestMappingInfo映射關係
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					//獲取方法的全限名
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				//判斷method上是否有CrossOrigin註解,把註解裏面的屬性封裝成CorsConfiguration,這個是做跨域訪問控制的
				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					//建立映射關係
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

在RequestMappingHandlerMapping中的幾個重要Map關係:
UrlLookup----->是url和RequestMappingInfo的映射集合
mappingLookup---->是RequestMapping和HandlerMethod(方法的封裝類)的映射關係
CorsLookup----->是HandlerMehod和CorsConfiguration(跨域註解@CrossOrigin的封裝類)
Registry------>是RequestMappingInfo和MappingRegistraion(RequestMappingInfo和HandlerMethod,Url,方法的全限名的包裝對象)的映射

瀏覽器發起請求

接着我們發送請求:根據Servlet規範,會請求到Servlet類中的service,然後service中會判斷具體判斷請求類型是是什麼,從而再會去調用doGet、doPost等方法。同樣在DispatcherServlet中也是相同請,當請求過來的時候,首先會調用到service方法,然後通過service方法調用到doservice方法,通過doservice方法繼續調用到doDispatch()方法,這個doDispatcher方法就是我們SpringMVC中的DispatcherServlet的核心流程了。在DispatcherServlet方法中,首先會判斷請求類型,判斷是否是文件傳輸類型,然後會根據請求的URL獲取方法的調用鏈,獲取調用鏈的方法爲getHandler方法,首先會根據URL在urlLookup集合中滿足請求的RequestMappingInfo集合,然後根據RequesMappingInfo對象在mappingLookup集合 中獲取到HandlerMethod對象(由於匹配的時候可能會存在多個,所以代碼內有一系列校驗),然後判斷HandlerMethod如果不爲空則會觸發HandlerMethod當中包裝類的getBean操作,之後就會返回HandlerMethod對象,接着會將HandlerMethod對象包裝成HandlerExecutionChain對象(調用鏈對象),主要是將本次請求所要經歷的攔截器過濾器,已經Controller中的方法封裝成調用鏈對象(在Spring的AOP中也有一個類似的調用鏈對象),如果請求中有跨域屬性,則將跨域的過濾器也加入到調用鏈中來,最後返回調用鏈。
在doDispatcher方法中獲取到了調用鏈後,首先會根據調用鏈中的HandlerMethod對象獲取到一個合適的適配器(這個適配主要作用是根據HandlerMethod中的方法,解析和封裝請求參數,以及封裝返回參數)。接着就是調用調用鏈中的前置攔截方法(如果有一個攔截器失敗,則會反向調用連接器中的後置攔截器方法,主要是由於後置攔截器一般是對於資源的釋放,所有必須執行,在這裏SpringMVC因爲要控制採用一個狀態標誌位來控制攔截器的調用),接着就會調用到Controller中的核心方法(這個方法比較重要,在這裏面它完成了參數的封裝匹配和調用),接着調用到中置攔截器,最後會對視圖ModelAndView的渲染,這視圖渲染結束後會調用到後置攔截器,就完成了請求的所有流程。

	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 {
				//判斷請求類型是否是文件上傳類型
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				//這個方法很重要,重點看---》獲取方法的調用鏈(裏面包括了攔截器、過濾器、HandlerMethod對象)
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				//獲取跟HandlerMethod匹配的HandlerAdapter對象
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				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;
					}
				}

				//前置過濾器,如果爲false則直接返回
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//調用到Controller具體方法,核心方法調用,主要是對於請求參數的封裝,以及方法的調用
				// Actually invoke the handler.
				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) {
			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);
				}
			}
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章