日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

口述完SpringMVC执行流程,面试官就让同事回家等消息了

發(fā)布時(shí)間:2023/12/10 javascript 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 口述完SpringMVC执行流程,面试官就让同事回家等消息了 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Srping MVC 執(zhí)行流程真的是老生常談的話題了,最近同事小剛出去面試,前面面試官相繼問(wèn)了幾個(gè) Spring 相關(guān)的問(wèn)題,但當(dāng)面試官問(wèn)他,你知道 Srping MVC 的執(zhí)行流程嗎?小剛嫻熟的巴拉巴拉回答完后,面試官就讓他回去等通知了…

Spring MVC 執(zhí)行流程

Spring MVC 執(zhí)行流程(圖片版):

Spring MVC 執(zhí)行流程(文字版):

  • 用戶發(fā)送請(qǐng)求到前端控制器 DispatcherServlet
  • DispatcherServlet 控制器接收到請(qǐng)求,然后調(diào)用 HandlerMapping 處理器映射器。
  • HandlerMapping 處理器映射器找到處理請(qǐng)求的 HandlerAdapter 處理器映射器。
  • DispatcherServlet 調(diào)用 HandlerAdapter 處理器適配器找到具體的處理器 Controller。
  • 執(zhí)行 Controller 進(jìn)行業(yè)務(wù)處理。
  • Controller 執(zhí)行完返回 ModelAndView 給 HandlerAdapter。
  • HandlerAdapter 將 Controller 執(zhí)行結(jié)果 ModelAndView 返回給 DispatcherServlet。
  • DispatcherServlet 查詢一個(gè)或多個(gè) ViewResoler 視圖解析器,找到 ModelAndView 指定的視圖 View。
  • ViewReslover 解析后返回具體 View。
  • DispatcherServlet 根據(jù)View 進(jìn)行渲染視圖。
  • DispatcherServlet 響應(yīng)結(jié)果給用戶。
  • 整個(gè)流程這樣回答下來(lái)應(yīng)該沒(méi)什么問(wèn)題,因?yàn)闊o(wú)論是書上還是面試題上基本都這么標(biāo)注的答案,但巧就巧在,同事小剛做的項(xiàng)目是前后端分離的項(xiàng)目(簡(jiǎn)歷中寫的),言外之意就是從第 6 步開(kāi)始,下面的回答基本就不對(duì)了,因?yàn)樵谇昂蠖朔蛛x項(xiàng)目中,最終返回給前端的數(shù)據(jù)是以 JSON 形式的,所以不存在什么視圖解析器一說(shuō)。

    這個(gè)時(shí)候需要變一個(gè)答法,就是直接返回 JSON 數(shù)據(jù)回去,可以使用 @ResponseBody 注解。

    到這基本也明白了為啥小剛答完這個(gè)問(wèn)題,面試官就讓他回去等消息了…

    Spring MVC 工作原理

    相信大家上方的執(zhí)行流程都背的滾瓜爛熟了…

    不知道大家是否有這種好奇,就是 @RequestMapping 注解的 Url 怎么就跟 Controller 關(guān)聯(lián)起來(lái)了呢?

    不管你好不好奇,接下來(lái)我們就假裝帶著這個(gè)好奇來(lái)看看怎么就關(guān)聯(lián)上了。

    從上邊的流程我們知道請(qǐng)求的入口是 DispatcherServlet ,那么我們來(lái)看一下這個(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í)在是沒(méi)耐心看下來(lái),以至于不得不精簡(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 是用來(lái)映射請(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è)屬性是用來(lái)標(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í)的排序過(guò)程 AnnotationAwareOrderComparator.sort(this.handlerMappings);,優(yōu)先使用高優(yōu)先級(jí)的 HandlerMapping。

    看到這,可能就會(huì)覺(jué)得,既然 HandlerMapping 有這么多的實(shí)現(xiàn)類,但是具體的某個(gè)實(shí)現(xiàn)類又是怎么初始化的呢?畢竟 HandlerMapping 的作用可是用來(lái)映射請(qǐng)求的,還沒(méi)看到具體實(shí)現(xiàn)過(guò)程呢…

    所以到這個(gè)時(shí)候,很顯然得進(jìn)行下去嘛,所以不得不找一個(gè)實(shí)現(xiàn)類來(lái)看看是如何具體初始化的,但是具體找哪個(gè)分析呢?在決定分析哪個(gè)之前,我們先了解一下 HadlerMapping 接口的繼承體系。

    HandlerMapping接口繼承體系

    這個(gè)體系比較龐大,我們著重看我標(biāo)注的紅框跟藍(lán)筐內(nèi)的內(nèi)容,通過(guò)1、2我們可以將 HadlerMapping 分為兩個(gè)體系,一是繼承自 AbstractUrlHandlerMapping,二是繼承 AbstractHandlerMethodMapping,因?yàn)殡S著版本的問(wèn)題(本文以Spring4.3.13為例),其中 AbstractUrlHandlerMapping 在目前大部分的項(xiàng)目已經(jīng)很少使用到了,所以接下來(lái)我們就重點(diǎn)分析AbstractHandlerMethodMapping,他就是我們經(jīng)常使用的@RequestMapping注解會(huì)使用到的方式。

    在分析 AbstractHandlerMethodMapping 之前,我們先分析下這個(gè)類的父類 AbstractHandlerMapping,不然有些方法就很懵逼。

    1、AbstractHandlerMapping概述

    已經(jīng)不想貼這個(gè)類的完整代碼了,感興趣的小伙伴自己點(diǎn)進(jìn)去看看吧,簡(jiǎn)單說(shuō)一下定義,AbstractHandlerMapping 是 HandlerMapping 的抽象實(shí)現(xiàn),采用模板模式設(shè)計(jì)了 HandlerMapping 的整體架構(gòu)。其定義了getHandlerInternal() 方法,該方法就是一個(gè)模版方法,根據(jù) request 來(lái)獲取相應(yīng)的 Handler,由它的兩個(gè)子類來(lái)具體實(shí)現(xiàn)該方法。然后再根據(jù) request 來(lái)獲取相應(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 {// 模板方法,暫無(wú)子類實(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ì)象,我們來(lái)看一下 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,接下來(lái)我們只著重分析 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 的,也就是說(shuō) urlLookup 變量中,一個(gè) url 可能對(duì)應(yīng)多個(gè) RequestMappingInfo。

    有了這個(gè)概念后我們?cè)賮?lái)看 lookupHandlerMethod() 方法,整個(gè)過(guò)程結(jié)果就是,根據(jù)入?yún)?lookupPath(可以看成是 url),從 request 中獲取到一個(gè)最符合的 RequestMappingInfo 對(duì)象,然后根據(jù)該對(duì)象再去獲取到 HandlerMethod 對(duì)象返回給父類 AbstractHandlerMapping。

    其實(shí)在 lookupHandlerMethod() 方法之前,還有一個(gè) mappingLookup、urlLookup 等參數(shù)初始化的過(guò)程,AbstractHandlerMethodMapping 實(shí)現(xiàn)了 InitializingBean 接口,當(dāng) Spring 容器啟動(dòng)時(shí)會(huì)自動(dòng)調(diào)用其 afterPropertiesSet() 方法,來(lái)完成 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);}}}// 模板方法,暫無(wú)子類實(shí)現(xiàn)handlerMethodsInitialized(getHandlerMethods()); }

    簡(jiǎn)單來(lái)說(shuō)這個(gè)方法就是進(jìn)行 HandlerMethod 的注冊(cè)操作,首先從 Spring MVC 的容器中獲取所有的 beanName 然后進(jìn)行過(guò)濾處理,注冊(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)行過(guò)濾,判斷是否包含 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 來(lái)找到并保存 URL 請(qǐng)求和處理函數(shù)間的 mapping 關(guān)系。

    以 AbstractHandlerMethodMapping 為例來(lái)具體看 HandlerMapping 的作用,首先拿到容器里所有的 bean,然后根據(jù)一定的規(guī)則篩選出 Handler,然后保存在 map 中,具體的篩選工作在子類中進(jìn)行:篩選的邏輯就是檢查類前是否存在 @Controller 或者 @RequestMapping 注解 ,然后在 detectHandlerMethods() 方法中負(fù)責(zé)將 Handler 保存在 map 中。

    1、用戶發(fā)送請(qǐng)求時(shí)會(huì)先從 DispathcherServler 的 doService 方法開(kāi)始,在該方法中會(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 方法來(lái)匹配每個(gè) HandlerMapping 對(duì)象,如果匹配成功會(huì)返回這個(gè) Handle 的處理鏈 HandlerExecutionChain 對(duì)象,在獲取該對(duì)象的內(nèi)部其實(shí)也獲取我們自定定義的攔截器,并執(zhí)行了其中的方法。

    4、執(zhí)行攔截器的 preHandle 方法,如果返回 false 執(zhí)行 afterCompletion 方法并理解返回

    5、通過(guò)上述獲取到了 HandlerExecutionChain 對(duì)象,通過(guò)該對(duì)象的 getHandler() 方法獲得一個(gè) object 通過(guò) HandlerAdapter 進(jìn)行封裝得到 HandlerAdapter 對(duì)象。

    6、該對(duì)象調(diào)用 handle 方法來(lái)執(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)挺亂的,整體描述的不是特別清楚,也很枯燥,之后通過(guò)閱讀 Spring MVC 整體源碼后再補(bǔ)充。

    本文首發(fā)于博客園:https://niceyoo.cnblogs.com/

    總結(jié)

    以上是生活随笔為你收集整理的口述完SpringMVC执行流程,面试官就让同事回家等消息了的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。