javascript
口述完SpringMVC执行流程,面试官就让同事回家等消息了
Srping MVC 執(zhí)行流程真的是老生常談的話題了,最近同事小剛出去面試,前面面試官相繼問了幾個(gè) Spring 相關(guān)的問題,但當(dāng)面試官問他,你知道 Srping MVC 的執(zhí)行流程嗎?小剛嫻熟的巴拉巴拉回答完后,面試官就讓他回去等通知了…
Spring MVC 執(zhí)行流程
Spring MVC 執(zhí)行流程(圖片版):
Spring MVC 執(zhí)行流程(文字版):
整個(gè)流程這樣回答下來應(yīng)該沒什么問題,因?yàn)闊o論是書上還是面試題上基本都這么標(biāo)注的答案,但巧就巧在,同事小剛做的項(xiàng)目是前后端分離的項(xiàng)目(簡(jiǎn)歷中寫的),言外之意就是從第 6 步開始,下面的回答基本就不對(duì)了,因?yàn)樵谇昂蠖朔蛛x項(xiàng)目中,最終返回給前端的數(shù)據(jù)是以 JSON 形式的,所以不存在什么視圖解析器一說。
這個(gè)時(shí)候需要變一個(gè)答法,就是直接返回 JSON 數(shù)據(jù)回去,可以使用 @ResponseBody 注解。
到這基本也明白了為啥小剛答完這個(gè)問題,面試官就讓他回去等消息了…
Spring MVC 工作原理
相信大家上方的執(zhí)行流程都背的滾瓜爛熟了…
不知道大家是否有這種好奇,就是 @RequestMapping 注解的 Url 怎么就跟 Controller 關(guān)聯(lián)起來了呢?
不管你好不好奇,接下來我們就假裝帶著這個(gè)好奇來看看怎么就關(guān)聯(lián)上了。
從上邊的流程我們知道請(qǐng)求的入口是 DispatcherServlet ,那么我們來看一下這個(gè)類:
@SuppressWarnings("serial") public class DispatcherServlet extends FrameworkServlet {public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);private static final Properties defaultStrategies;static {try {ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);}catch (IOException ex) {throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());}}/** Detect all HandlerMappings or just expect "handlerMapping" bean? */private boolean detectAllHandlerMappings = true;/** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */private boolean detectAllHandlerAdapters = true;/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */private boolean detectAllHandlerExceptionResolvers = true;/** Detect all ViewResolvers or just expect "viewResolver" bean? */private boolean detectAllViewResolvers = true;/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/private boolean throwExceptionIfNoHandlerFound = false;/** Perform cleanup of request attributes after include request? */private boolean cleanupAfterInclude = true;/** MultipartResolver used by this servlet */private MultipartResolver multipartResolver;/** LocaleResolver used by this servlet */private LocaleResolver localeResolver;/** ThemeResolver used by this servlet */private ThemeResolver themeResolver;/** List of HandlerMappings used by this servlet */private List<HandlerMapping> handlerMappings;/** List of HandlerAdapters used by this servlet */private List<HandlerAdapter> handlerAdapters;/** List of HandlerExceptionResolvers used by this servlet */private List<HandlerExceptionResolver> handlerExceptionResolvers;/** RequestToViewNameTranslator used by this servlet */private RequestToViewNameTranslator viewNameTranslator;private FlashMapManager flashMapManager;/** List of ViewResolvers used by this servlet */private List<ViewResolver> viewResolvers;public DispatcherServlet() {super();}public DispatcherServlet(WebApplicationContext webApplicationContext) {super(webApplicationContext);}@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);} }這個(gè)類真的是太長(zhǎng)了,實(shí)在是沒耐心看下來,以至于不得不精簡(jiǎn)了一部分(上方為精簡(jiǎn)后的),在這里面我們可以看到一些熟悉的面孔:
- HandlerMapping:用于handlers映射請(qǐng)求和一系列的對(duì)于攔截器的前處理和后處理,大部分用@Controller注解。
- HandlerAdapter:幫助DispatcherServlet處理映射請(qǐng)求處理程序的適配器,而不用考慮實(shí)際調(diào)用的是 哪個(gè)處理程序
- ViewResolver:根據(jù)實(shí)際配置解析實(shí)際的View類型。
- ThemeResolver:解決Web應(yīng)用程序可以使用的主題,例如提供個(gè)性化布局。
- MultipartResolver:解析多部分請(qǐng)求,以支持從HTML表單上傳文件。
既然 HandlerMapping 是用來映射請(qǐng)求的,然后我們就繼續(xù)朝著這個(gè)方向走,在上方 DispatcherServlet 中找到 HandlerMapping 相關(guān)的代碼,然后我們就找到了這個(gè)集合:
private List<HandlerMapping> handlerMappings;
接著看看在哪給這個(gè) handlerMappings 集合賦值的,然后就找到了如下方法:
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerMappings);}} else {try {HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);} catch (NoSuchBeanDefinitionException var3) {}}if (this.handlerMappings == null) {this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}}簡(jiǎn)單分析一下 initHandlerMappings() 這段方法,首先我們需要先了解 HandlerMapping 其實(shí)是一個(gè)接口類,然后這個(gè)接口類就定義了一個(gè)方法 getHandler() ,這個(gè)方法也很簡(jiǎn)單,就是根據(jù)請(qǐng)求的 request,獲取 HandlerExecutionChain 對(duì)象:
public interface HandlerMapping {String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";@NullableHandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception; }Spring MVC 提供了許多 HandlerMapping 的實(shí)現(xiàn),我們可以點(diǎn)進(jìn)去看看這個(gè) HandlerMapping 有多少實(shí)現(xiàn):
大約有19個(gè)子類實(shí)現(xiàn)了 HandlerMapping 接口,默認(rèn)使用的是 BeanNameUrlHandlerMapping(可以根據(jù) Bean 的 name 屬性映射到 URL 中)。我們?cè)倩氐?initHandlerMappings() 方法,我們可以看到在賦值的時(shí)候有個(gè) if (this.detectAllHandlerMappings) 屬性的判斷,這個(gè)屬性是用來標(biāo)記是否只期望 Srping MVC 只加載指定的 HandlerMappring 的,如果修改為 fasle ,Spring MVC 就只會(huì)查找名為 “handlerMapping” 的 bean,并作為當(dāng)前系統(tǒng)的唯一的 HandlerMapping。
而正常情況為 true 時(shí),就會(huì)加載所有 HandlerMapping 的實(shí)現(xiàn)類,加載之后還有個(gè)使用優(yōu)先級(jí)的排序過程 AnnotationAwareOrderComparator.sort(this.handlerMappings);,優(yōu)先使用高優(yōu)先級(jí)的 HandlerMapping。
看到這,可能就會(huì)覺得,既然 HandlerMapping 有這么多的實(shí)現(xiàn)類,但是具體的某個(gè)實(shí)現(xiàn)類又是怎么初始化的呢?畢竟 HandlerMapping 的作用可是用來映射請(qǐng)求的,還沒看到具體實(shí)現(xiàn)過程呢…
所以到這個(gè)時(shí)候,很顯然得進(jìn)行下去嘛,所以不得不找一個(gè)實(shí)現(xiàn)類來看看是如何具體初始化的,但是具體找哪個(gè)分析呢?在決定分析哪個(gè)之前,我們先了解一下 HadlerMapping 接口的繼承體系。
HandlerMapping接口繼承體系
這個(gè)體系比較龐大,我們著重看我標(biāo)注的紅框跟藍(lán)筐內(nèi)的內(nèi)容,通過1、2我們可以將 HadlerMapping 分為兩個(gè)體系,一是繼承自 AbstractUrlHandlerMapping,二是繼承 AbstractHandlerMethodMapping,因?yàn)殡S著版本的問題(本文以Spring4.3.13為例),其中 AbstractUrlHandlerMapping 在目前大部分的項(xiàng)目已經(jīng)很少使用到了,所以接下來我們就重點(diǎn)分析AbstractHandlerMethodMapping,他就是我們經(jīng)常使用的@RequestMapping注解會(huì)使用到的方式。
在分析 AbstractHandlerMethodMapping 之前,我們先分析下這個(gè)類的父類 AbstractHandlerMapping,不然有些方法就很懵逼。
1、AbstractHandlerMapping概述
已經(jīng)不想貼這個(gè)類的完整代碼了,感興趣的小伙伴自己點(diǎn)進(jìn)去看看吧,簡(jiǎn)單說一下定義,AbstractHandlerMapping 是 HandlerMapping 的抽象實(shí)現(xiàn),采用模板模式設(shè)計(jì)了 HandlerMapping 的整體架構(gòu)。其定義了getHandlerInternal() 方法,該方法就是一個(gè)模版方法,根據(jù) request 來獲取相應(yīng)的 Handler,由它的兩個(gè)子類來具體實(shí)現(xiàn)該方法。然后再根據(jù) request 來獲取相應(yīng)的 interceptors,整合從子類獲取的 Handler,組成 HandlerExecutionChain 對(duì)象返回。
@Nullable protected abstract Object getHandlerInternal(HttpServletRequest var1) throws Exception;2、AbstractHandlerMapping初始化
AbstractHandlerMapping 繼承了 WebApplicationObjectSupport(獲取Spring的ApplicationContext方式之一,可以看做java類獲取Spring容器的Bean),初始化時(shí)會(huì)自動(dòng)調(diào)用模板方法 initApplicationContext,具體如下:
@Override protected void initApplicationContext() throws BeansException {// 模板方法,暫無子類實(shí)現(xiàn)extendInterceptors(this.interceptors);// 從容器中獲取實(shí)現(xiàn)了MappedInterceptor接口的對(duì)象,添加到adaptedInterceptors列表中detectMappedInterceptors(this.adaptedInterceptors);// 將interceptors(由子類添加)中的對(duì)象封裝后添加到adaptedInterceptors列表中initInterceptors(); }3、AbstractHandlerMapping的使用
AbstractHandlerMapping 繼承自 HandlerMapping ,實(shí)現(xiàn)了其 getHandler() 方法,我們上邊也提到該方法就是根據(jù)請(qǐng)求的 request,獲取 HandlerExecutionChain 對(duì)象,我們來看一下 AbstractHandlerMapping 中的實(shí)現(xiàn):
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 模板模式方法,具體由子類實(shí)現(xiàn)Object handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}// 根據(jù)request從adaptedInterceptors中選擇匹配的interceptors,與handler一起封裝成HandlerExecutionChain對(duì)象HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);return executionChain; }到這就是交給子類去完成了,分別是 AbstractUrlHandlerMapping 和 AbstractHandlerMethodMapping,接下來我們只著重分析 AbstractHandlerMethodMapping 類。
AbstractHandlerMethodMapping
首先實(shí)現(xiàn)了父類 getHandlerInternal() 方法:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {// 獲得請(qǐng)求的路徑String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);request.setAttribute(LOOKUP_PATH, lookupPath);// 獲得讀鎖this.mappingRegistry.acquireReadLock();HandlerMethod var4;try {// 獲得 HandlerMethod 對(duì)象HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;} finally {// 釋放鎖this.mappingRegistry.releaseReadLock();}return var4; }重點(diǎn)在于 lookupHandlerMethod() 方法,如下為詳細(xì)代碼:
@Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {// Match數(shù)組,用于存儲(chǔ)匹配上當(dāng)前請(qǐng)求的結(jié)果List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();// 優(yōu)先級(jí),基于直接 URL 的 Mapprings 進(jìn)行匹配List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);if (directPathMatches != null) {this.addMatchingMappings(directPathMatches, matches, request);}// 掃描注冊(cè)表的 Mappings 進(jìn)行匹配if (matches.isEmpty()) {this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}// 如果匹配到,則獲取最佳匹配的 Match 對(duì)象的 handlerMethod 屬性if (!matches.isEmpty()) {// 創(chuàng)建 MathComparator 對(duì)象Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));// 排序 matches 結(jié)果matches.sort(comparator);// 獲取首個(gè) Match 對(duì)象AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);// 處理存在多個(gè) Match 對(duì)象的情況if (matches.size() > 1) {if (this.logger.isTraceEnabled()) {this.logger.trace(matches.size() + " matching mappings: " + matches);}if (CorsUtils.isPreFlightRequest(request)) {return PREFLIGHT_AMBIGUOUS_MATCH;}// 比較 bestMatch 和 secondBestMatch,如果相等則拋出 IllegalStateException 異常AbstractHandlerMethodMapping<T>.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.handlerMethod.getMethod();Method m2 = secondBestMatch.handlerMethod.getMethod();String uri = request.getRequestURI();throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");}}request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);// 處理首個(gè) Match 對(duì)象this.handleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;} else {// 如果匹配不到,則處理不匹配的情況return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);} }在分析 lookupHandlerMethod() 方法的整體思路之前,我們還得知曉 AbstractHandlerMethodMapping 的內(nèi)部類 MappingRegistry。MappingRegistry 類中定義了兩個(gè)比較重要的變量,Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>() 和 MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>()。其中 mappingLookup 變量保存了 RequestMappingInfo 與 HandlerMethod 的一一對(duì)應(yīng)關(guān)系,而 urlLookup 變量則保存了 url 與 RequestMappingInfo 的對(duì)應(yīng)關(guān)系,需要注意的是 MultiValueMap 類型的變量是可以一個(gè) key 對(duì)應(yīng)多個(gè) value 的,也就是說 urlLookup 變量中,一個(gè) url 可能對(duì)應(yīng)多個(gè) RequestMappingInfo。
有了這個(gè)概念后我們?cè)賮砜?lookupHandlerMethod() 方法,整個(gè)過程結(jié)果就是,根據(jù)入?yún)?lookupPath(可以看成是 url),從 request 中獲取到一個(gè)最符合的 RequestMappingInfo 對(duì)象,然后根據(jù)該對(duì)象再去獲取到 HandlerMethod 對(duì)象返回給父類 AbstractHandlerMapping。
其實(shí)在 lookupHandlerMethod() 方法之前,還有一個(gè) mappingLookup、urlLookup 等參數(shù)初始化的過程,AbstractHandlerMethodMapping 實(shí)現(xiàn)了 InitializingBean 接口,當(dāng) Spring 容器啟動(dòng)時(shí)會(huì)自動(dòng)調(diào)用其 afterPropertiesSet() 方法,來完成 handlerMethod 的注冊(cè)操作,但是該方法最終又交給 initHandlerMethods() 方法完成具體的初始化:
protected void initHandlerMethods() {if (logger.isDebugEnabled()) {logger.debug("Looking for request mappings in application context: " + getApplicationContext());}// 從springMVC容器中獲取所有的beanNamString[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));// 注冊(cè)從容器中獲取的beanNamefor (String beanName : beanNames) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {Class<?> beanType = null;try {beanType = getApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);}}if (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}}}// 模板方法,暫無子類實(shí)現(xiàn)handlerMethodsInitialized(getHandlerMethods()); }簡(jiǎn)單來說這個(gè)方法就是進(jìn)行 HandlerMethod 的注冊(cè)操作,首先從 Spring MVC 的容器中獲取所有的 beanName 然后進(jìn)行過濾處理,注冊(cè) URL 和實(shí)現(xiàn)方法 HandlerMethod 的對(duì)應(yīng)關(guān)系。
在 initHandlerMethods() 方法中我們主要關(guān)注兩個(gè)方法 isHandler(beanType) 與 detectHandlerMethods(beanName) ,其中 isHandler(beanType) 方法由子類 RequestMappingHandlerMapping 實(shí)現(xiàn),用于對(duì) bean 進(jìn)行過濾,判斷是否包含 Controller 或者 RequestMapping 注解。
@Override protected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }而 detectHandlerMethods(beanName) 方法,則根據(jù)篩選出的 bean,進(jìn)行一系列的注冊(cè),最終實(shí)現(xiàn)是在 registerHandlerMethod() 方法:
protected void detectHandlerMethods(final Object handler) {Class<?> handlerType = (handler instanceof String ?getApplicationContext().getType((String) handler) : handler.getClass());// CGLib動(dòng)態(tài)代理的特殊處理final Class<?> userType = ClassUtils.getUserClass(handlerType);// methods包含了bean中所有符合條件的method與相關(guān)的RequestMappingInfo鍵值對(duì)Map<Method, T> methods = MethodIntrospector.selectMethods(userType,new MethodIntrospector.MetadataLookup<T>() {@Overridepublic T inspect(Method method) {try {// 如果method有@RequestMapping注解,則返回由注解得到的封裝好的RequestMappingInfo對(duì)象,否則返回nullreturn getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}}});if (logger.isDebugEnabled()) {logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);}for (Map.Entry<Method, T> entry : methods.entrySet()) {Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);T mapping = entry.getValue();// 注冊(cè) beanName,Method及創(chuàng)建的RequestMappingInfo之間的關(guān)系registerHandlerMethod(handler, invocableMethod, mapping);} }detectHandlerMethods() 方法中的 getMappingForMethod() 方法是在子類 RequestMappingHandlerMapping 中實(shí)現(xiàn)的,具體實(shí)現(xiàn)就是創(chuàng)建一個(gè) RequestMappingInfo:
/*** Uses method and type-level @{@link RequestMapping} annotations to create* the RequestMappingInfo.* @return the created RequestMappingInfo, or {@code null} if the method* does not have a {@code @RequestMapping} annotation.* @see #getCustomMethodCondition(Method)* @see #getCustomTypeCondition(Class)*/ @Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {RequestMappingInfo info = createRequestMappingInfo(method);if (info != null) {RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);if (typeInfo != null) {info = typeInfo.combine(info);}}return info; }/*** Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},* supplying the appropriate custom {@link RequestCondition} depending on whether* the supplied {@code annotatedElement} is a class or method.* @see #getCustomTypeCondition(Class)* @see #getCustomMethodCondition(Method)*/ private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);RequestCondition<?> condition = (element instanceof Class ?getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }detectHandlerMethods() 方法中的 registerHandlerMethod() 方法的操作是注冊(cè) beanName,Method 及創(chuàng)建的 RequestMappingInfo 之間的關(guān)系:
protected void registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method); }到這就簡(jiǎn)單實(shí)現(xiàn)了將 url 和 HandlerMethod 的對(duì)應(yīng)關(guān)系注冊(cè)到 mappingRegistry 中了。
private final AbstractHandlerMethodMapping<T>.MappingRegistry mappingRegistry = new AbstractHandlerMethodMapping.MappingRegistry();最后總結(jié)
Spring MVC 執(zhí)行流程
在描述 Spring MVC 執(zhí)行流程的時(shí)候需要注意,只有在以前使用 jsp、themlef 等模板引擎的時(shí)候,我們會(huì)把前端界面放在后端的工程里,然后在 controller 執(zhí)行完業(yè)務(wù)邏輯之后會(huì)返回一個(gè)界面模版名稱,也就是ModelAndView,然后 DispatherServlet 將 ModelAndView 傳遞給 ViewReslover 視圖解析器,視圖解析器解析后返回具體的 View,然后對(duì) html 界面做一個(gè)渲染。
如果返回的是一個(gè)json串的話,也就是前后端分離的項(xiàng)目,那么只需要返回json數(shù)據(jù)即可。
Spring MVC 工作原理
Spring MVC 使用 HandlerMappring 來找到并保存 URL 請(qǐng)求和處理函數(shù)間的 mapping 關(guān)系。
以 AbstractHandlerMethodMapping 為例來具體看 HandlerMapping 的作用,首先拿到容器里所有的 bean,然后根據(jù)一定的規(guī)則篩選出 Handler,然后保存在 map 中,具體的篩選工作在子類中進(jìn)行:篩選的邏輯就是檢查類前是否存在 @Controller 或者 @RequestMapping 注解 ,然后在 detectHandlerMethods() 方法中負(fù)責(zé)將 Handler 保存在 map 中。
1、用戶發(fā)送請(qǐng)求時(shí)會(huì)先從 DispathcherServler 的 doService 方法開始,在該方法中會(huì)將 ApplicationContext、localeResolver、themeResolver 等對(duì)象添加到 request 中,緊接著就是調(diào)用 doDispatch 方法。
2、進(jìn)入 doDispatch 方法后首先會(huì)檢查該請(qǐng)求是否是文件上傳的請(qǐng)求,(校驗(yàn)的規(guī)則是是否是post并且contenttType是否為multipart/為前綴)即調(diào)用的是 checkMultipart 方法,如果是的話將 request 包裝成 MultipartHttpServletRequest。
3、然后調(diào)用 getHandler 方法來匹配每個(gè) HandlerMapping 對(duì)象,如果匹配成功會(huì)返回這個(gè) Handle 的處理鏈 HandlerExecutionChain 對(duì)象,在獲取該對(duì)象的內(nèi)部其實(shí)也獲取我們自定定義的攔截器,并執(zhí)行了其中的方法。
4、執(zhí)行攔截器的 preHandle 方法,如果返回 false 執(zhí)行 afterCompletion 方法并理解返回
5、通過上述獲取到了 HandlerExecutionChain 對(duì)象,通過該對(duì)象的 getHandler() 方法獲得一個(gè) object 通過 HandlerAdapter 進(jìn)行封裝得到 HandlerAdapter 對(duì)象。
6、該對(duì)象調(diào)用 handle 方法來執(zhí)行 Controller 中的方法,然后根據(jù)類型返回不同的結(jié)果(如JSON、ModelAndView),該對(duì)象如果返回一個(gè) ModelAndView 給 DispatcherServlet。
7、DispatcherServlet 借助 ViewResolver 完成邏輯試圖名到真實(shí)視圖對(duì)象的解析,得到 View 后 DispatcherServlet 使用這個(gè) View 對(duì) ModelAndView 中的模型數(shù)據(jù)進(jìn)行視圖渲染。
工作原理寫著寫著發(fā)現(xiàn)挺亂的,整體描述的不是特別清楚,也很枯燥,之后通過閱讀 Spring MVC 整體源碼后再補(bǔ)充。
本文首發(fā)于博客園:https://niceyoo.cnblogs.com/
總結(jié)
以上是生活随笔為你收集整理的口述完SpringMVC执行流程,面试官就让同事回家等消息了的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Git合并分支操作
- 下一篇: 气象数据源-要素、数据集、空间分辨率、网