久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

看透 Spring MVC 源代碼分析與實(shí)踐——俯視 Spring MVC

 Bladexu的文庫(kù) 2017-11-03

Spring MVC

看透 Spring MVC 源代碼分析與實(shí)踐——俯視 Spring MVC

spring-mvc

Spring MVC 之初體驗(yàn)

環(huán)境搭建

在 IDEA 中新建一個(gè) web 項(xiàng)目,用 Maven 管理項(xiàng)目的話,,在 pom.xml 中加入 Spring MVC 和 Servlet 依賴即可,。

org.springframework spring-webmvc 4.3.9.RELEASE javax.servlet javax.servlet-api 3.1.0 provided

Spring MVC 簡(jiǎn)單配置

  • 在 web.xml 中配置 Servlet

  • 創(chuàng)建 Spring MVC 的 xml 配置文件

  • 創(chuàng)建 Controller 和 View

1,、web.xml

spring org.springframework.web.servlet.DispatcherServlet 1 spring *.do org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:config/applicationContext.xml

2,、spring-servlet.xml

http://www./schema/context/spring-context-3.0.xsd'>   

3,、Controller

package controller;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import entity.User;@Controller //類似Struts的Actionpublic class TestController { @RequestMapping('/test/login.do') // 請(qǐng)求url地址映射,,類似Struts的action-mapping public String testLogin(@RequestParam(value='username')String username, String password, HttpServletRequest request) { // @RequestParam是指請(qǐng)求url地址映射中必須含有的參數(shù)(除非屬性 required=false, 默認(rèn)為 true) // @RequestParam可簡(jiǎn)寫為:@RequestParam('username') if (!'admin'.equals(username) || !'admin'.equals(password)) { return 'loginError'; // 跳轉(zhuǎn)頁(yè)面路徑(默認(rèn)為轉(zhuǎn)發(fā)),,該路徑不需要包含spring-servlet配置文件中配置的前綴和后綴 } return 'loginSuccess'; } @RequestMapping('/test/login2.do') public ModelAndView testLogin2(String username, String password, int age){ // request和response不必非要出現(xiàn)在方法中,如果用不上的話可以去掉 // 參數(shù)的名稱是與頁(yè)面控件的name相匹配,,參數(shù)類型會(huì)自動(dòng)被轉(zhuǎn)換 if (!'admin'.equals(username) || !'admin'.equals(password) || age < 5)="" {="" return="" new="" modelandview('loginerror');="" 手動(dòng)實(shí)例化modelandview完成跳轉(zhuǎn)頁(yè)面(轉(zhuǎn)發(fā)),,效果等同于上面的方法返回字符串="" }="" return="" new="" modelandview(new="" redirectview('../index.jsp'));="" 采用重定向方式跳轉(zhuǎn)頁(yè)面="" 重定向還有一種簡(jiǎn)單寫法="" return="" new="" modelandview('redirect:../index.jsp');="" }="" @requestmapping('/test/login3.do')="" public="" modelandview="" testlogin3(user="" user)="" {="" 同樣支持參數(shù)為表單對(duì)象,類似于struts的actionform,,user不需要任何配置,,直接寫即可="" string="" username="user.getUsername();" string="" password="user.getPassword();" int="" age="user.getAge();" if="" (!'admin'.equals(username)="" ||="" !'admin'.equals(password)="" ||="" age="">< 5)="" {="" return="" new="" modelandview('loginerror');="" }="" return="" new="" modelandview('loginsuccess');="" }="" @resource(name='loginService' )="" 獲取applicationcontext.xml中bean的id為loginservice的,并注入="" private="" loginservice="" loginservice;="" 等價(jià)于spring傳統(tǒng)注入方式寫get和set方法,,這樣的好處是簡(jiǎn)潔工整,,省去了不必要得代碼="" @requestmapping('/test/login4.do')="" public="" string="" testlogin4(user="" user)="" {="" if="" (loginservice.login(user)="=" false)="" {="" return="" 'loginerror';="" }="" return="" 'loginsuccess';="">

@RequestMapping 可以寫在方法上,也可以寫在類上,,上面代碼方法上的 RequestMapping 都含有 /test ,, 那么我們就可以將其抽出直接寫在類上,,那么方法里面就不需要寫 /test了。

如下即可:

@Controller@RequestMapping('/test')public class TestController { @RequestMapping('/login.do') // 請(qǐng)求url地址映射,,類似Struts的action-mapping public String testLogin(@RequestParam(value='username')String username, String password, HttpServletRequest request) { // @RequestParam是指請(qǐng)求url地址映射中必須含有的參數(shù)(除非屬性 required=false, 默認(rèn)為 true) // @RequestParam可簡(jiǎn)寫為:@RequestParam('username') if (!'admin'.equals(username) || !'admin'.equals(password)) { return 'loginError'; // 跳轉(zhuǎn)頁(yè)面路徑(默認(rèn)為轉(zhuǎn)發(fā)),,該路徑不需要包含spring-servlet配置文件中配置的前綴和后綴 } return 'loginSuccess'; } //省略其他的}

上面的代碼方法的參數(shù)中可以看到有一個(gè) @RequestParam 注解,其實(shí)還有 @PathVariable ,。這兩個(gè)的區(qū)別是啥呢,?

  • @PathVariable 標(biāo)記在方法的參數(shù)上,利用它標(biāo)記的參數(shù)可以利用請(qǐng)求路徑傳值,。

  • @RequestParam是指請(qǐng)求url地址映射中必須含有的參數(shù)(除非屬性 required=false, 默認(rèn)為 true)

看如下例子:

@RequestMapping('/user/{userId}') // 請(qǐng)求url地址映射public String userinfo(Model model, @PathVariable('userId') int userId, HttpSession session) { System.out.println('進(jìn)入 userinfo 頁(yè)面'); //判斷是否有用戶登錄 User user1 = (User) session.getAttribute('user'); if (user1 == null) { return 'login'; } User user = userService.selectUserById(userId); model.addAttribute('user', user); return 'userinfo'; }

上面例子中如果瀏覽器請(qǐng)求的是 /user/1 的時(shí)候,,就表示此時(shí)的用戶 id 為 1,此時(shí)就會(huì)先從 session 中查找是否有 “user” 屬性,,如果有的話,,就代表用戶此時(shí)處于登錄的狀態(tài),如果沒(méi)有的話,,就會(huì)讓用戶返回到登錄頁(yè)面,,這種機(jī)制在各種網(wǎng)站經(jīng)常會(huì)使用的,然后根據(jù)這個(gè) id = 1 ,,去查找用戶的信息,,然后把查找的 “user” 放在 model 中,然后返回用戶詳情頁(yè)面,,最后在頁(yè)面中用 $!{user.name} 獲取用戶的名字,,同樣的方式可以獲取用戶的其他信息,把所有的用戶詳情信息展示出來(lái),。

創(chuàng)建 Spring MVC 之器

Spring MVC 核心 Servlet 架構(gòu)圖如下:

看透 Spring MVC 源代碼分析與實(shí)踐——俯視 Spring MVC

spring-mvc1

Java 中常用的 Servlet 我在另外一篇文章寫的很清楚了,,有興趣的請(qǐng)看:通過(guò)源碼詳解 Servlet ,這里我就不再解釋了,。

這里主要講 Spring 中的 HttpServletBean,、FrameworkServlet、DispatcherServlet 這三個(gè)類的創(chuàng)建過(guò)程,。

通過(guò)上面的圖,,可以看到這三個(gè)類直接實(shí)現(xiàn)三個(gè)接口:EnvironmentCapable、EnvironmentAware,、ApplicationContextAware,。下面我們直接看下這三個(gè)接口的內(nèi)部是怎樣寫的。

EnvironmentCapable.java

public interface EnvironmentCapable { //返回組件的環(huán)境,,可能返回 null 或者默認(rèn)環(huán)境 @Nullable Environment getEnvironment();}

EnvironmentAware.java

public interface EnvironmentAware extends Aware { //設(shè)置組件的運(yùn)行環(huán)境 void setEnvironment(Environment environment);}

ApplicationContextAware.java

public interface ApplicationContextAware extends Aware { //設(shè)置運(yùn)行對(duì)象的應(yīng)用上下文 //當(dāng)類實(shí)現(xiàn)這個(gè)接口后,,這個(gè)類可以獲取ApplicationContext中所有的bean,也就是說(shuō)這個(gè)類可以直接獲取Spring配置文件中所有有引用到的bean對(duì)象 void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}

怎么使用這個(gè)這個(gè)接口呢?

參考文章:org.springframework.context.ApplicationContextAware使用理解

HttpServletBean

這里就直接看其中最重要的 init() 方法的代碼了:

/*** 將配置參數(shù)映射到此servlet的bean屬性,,并調(diào)用子類初始化,。* 如果 bean 配置不合法(或者需要的參數(shù)丟失)或者子類初始化發(fā)生錯(cuò)誤,那么就會(huì)拋出 ServletException 異常*/@Overridepublic final void init() throws ServletException { //日志代碼刪除了 // 從init參數(shù)設(shè)置bean屬性,。 //獲得web.xml中的contextConfigLocation配置屬性,,就是spring MVC的配置文件 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); //獲取服務(wù)器的各種信息 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //模板方法,可以在子類中調(diào)用,,做一些初始化工作,bw代表DispatcherServelt initBeanWrapper(bw); //將配置的初始化值設(shè)置到DispatcherServlet中 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { //日志代碼 throw ex; } } // Let subclasses do whatever initialization they like. //模板方法,,子類初始化的入口方法 initServletBean(); //日志代碼刪除了}

FrameworkServlet

其中重要方法如下:里面也就兩句關(guān)鍵代碼,,日志代碼我直接刪掉了

protected final void initServletBean() throws ServletException { //日志代碼刪除了 long startTime = System.currentTimeMillis(); //就是 try 語(yǔ)句里面有兩句關(guān)鍵代碼 try { //初始化 webApplicationContext this.webApplicationContext = initWebApplicationContext(); //模板方法, initFrameworkServlet(); } catch (ServletException ex) { this.logger.error('Context initialization failed', ex); throw ex; } catch (RuntimeException ex) { this.logger.error('Context initialization failed', ex); throw ex; } //日志代碼刪除了 }

再來(lái)看看上面代碼中調(diào)用的 initWebApplicationContext() 方法

protected WebApplicationContext initWebApplicationContext() { //獲取 rootContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // 上下文實(shí)例在構(gòu)造時(shí)注入 - >使用它 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // 如果上下文尚未刷新 -> 提供諸如設(shè)置父上下文,,設(shè)置應(yīng)用程序上下文ID等服務(wù) if (cwac.getParent() == null) { // 上下文實(shí)例被注入沒(méi)有顯式的父類 -> 將根應(yīng)用程序上下文(如果有的話可能為null)設(shè)置為父級(jí) cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 當(dāng) WebApplicationContext 已經(jīng)存在 ServletContext 中時(shí),,通過(guò)配置在 servlet 中的 ContextAttribute 參數(shù)獲取 wac = findWebApplicationContext(); } if (wac == null) { // 如果 WebApplicationContext 還沒(méi)有創(chuàng)建,則創(chuàng)建一個(gè) wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 當(dāng) ContextRefreshedEvent 事件沒(méi)有觸發(fā)時(shí)調(diào)用此方法,,模板方法,,可以在子類重寫 onRefresh(wac); } if (this.publishContext) { // 將 ApplicationContext 保存到 ServletContext 中去 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug('Published WebApplicationContext of servlet '' + getServletName() + '' as ServletContext attribute with name [' + attrName + ']'); } } return wac; }

initWebApplicationContext 方法做了三件事:

  • 獲取 Spring 的根容器 rootContext

  • 設(shè)置 webApplicationContext 并根據(jù)情況調(diào)用 onRefresh 方法

  • 將 webApplicationContext 設(shè)置到 ServletContext 中

這里在講講上面代碼中的 wac == null 的幾種情況:

1)、當(dāng) WebApplicationContext 已經(jīng)存在 ServletContext 中時(shí),,通過(guò)配置在 servlet 中的 ContextAttribute 參數(shù)獲取,,調(diào)用的是 findWebApplicationContext() 方法

protected WebApplicationContext findWebApplicationContext() { String attrName = getContextAttribute(); if (attrName == null) { return null; } WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException('No WebApplicationContext found: initializer not registered?'); } return wac; }

2)、如果 WebApplicationContext 還沒(méi)有創(chuàng)建,,調(diào)用的是 createWebApplicationContext 方法

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //獲取創(chuàng)建類型 Class contextClass = getContextClass(); //刪除了打印日志代碼 //檢查創(chuàng)建類型 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( 'Fatal initialization error in servlet with name '' + getServletName() + '': custom WebApplicationContext class [' + contextClass.getName() + '] is not of type ConfigurableWebApplicationContext'); } //具體創(chuàng)建 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); //并設(shè)置的 contextConfigLocation 參數(shù)傳給 wac,,默認(rèn)是 WEB-INFO/[ServletName]-Servlet.xml wac.setConfigLocation(getContextConfigLocation()); //調(diào)用的是下面的方法 configureAndRefreshWebApplicationContext(wac); return wac; }protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); wac.refresh(); }

里面還有 doXXX() 方法,大家感興趣的可以去看看,。

DispatcherServlet

DispatcherServlet 繼承自 FrameworkServlet,,onRefresh 方法是 DispatcherServlet 的入口方法,在 initStrategies 方法中調(diào)用了 9 個(gè)初始化的方法,。

看透 Spring MVC 源代碼分析與實(shí)踐——俯視 Spring MVC

onrefresh

這里分析其中一個(gè)初始化方法:initLocaleResolver() 方法

private void initLocaleResolver(ApplicationContext context) { try { //在 context 中獲取 this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); //刪除了打印日志的代碼 } catch (NoSuchBeanDefinitionException ex) { //使用默認(rèn)的策略 this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); //刪除了打印日志的代碼 } }

查看默認(rèn)策略代碼:

protected T getDefaultStrategy(ApplicationContext context, Class strategyInterface) { //調(diào)用 getDefaultStrategies 方法 List strategies = getDefaultStrategies(context, strategyInterface); if (strategies.size() != 1) { throw new BeanInitializationException( 'DispatcherServlet needs exactly 1 strategy for interface [' + strategyInterface.getName() + ']'); } return strategies.get(0); } /** * Create a List of default strategy objects for the given strategy interface. *

The default implementation uses the 'DispatcherServlet.properties' file (in the same * package as the DispatcherServlet class) to determine the class names. It instantiates * the strategy objects through the context's BeanFactory. */ @SuppressWarnings('unchecked') protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); //根據(jù)策略接口的名字從 defaultStrategies 獲取所需策略的類型 String value = defaultStrategies.getProperty(key); if (value != null) { //如果有多個(gè)默認(rèn)值的話,,就以逗號(hào)分隔為數(shù)組 String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List 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( 'Error loading DispatcherServlet's default strategy class [' + className + '] for interface [' + key + ']: problem with class file or dependent class', err); } } return strategies; } else { return new LinkedList<>(); } }

其他幾個(gè)方法大概也類似,我就不再寫了,。

小結(jié)

主要講了 Spring MVC 自身創(chuàng)建過(guò)程,,分析了 Spring MVC 中 Servlet 的三個(gè)層次:HttpServletBean、FrameworkServlet 和 DispatcherServlet,。HttpServletBean 繼承自 Java 的 HttpServlet,,其作用是將配置的參數(shù)設(shè)置到相應(yīng)的屬性上;FrameworkServlet 初始化了 WebApplicationContext,;DispatcherServlet 初始化了自身的 9 個(gè)組件,。

Spring MVC 之用

分析 Spring MVC 是怎么處理請(qǐng)求的。首先分析 HttpServletBean、FrameworkServlet 和 DispatcherServlet 這三個(gè) Servlet 的處理過(guò)程,,最后分析 doDispatcher 的結(jié)構(gòu),。

HttpServletBean

參與了創(chuàng)建工作,并沒(méi)有涉及請(qǐng)求的處理,。

FrameworkServlet

在類中的 service() ,、doGet()、doPost(),、doPut(),、doDelete()、doOptions(),、doTrace() 這些方法中可以看到都調(diào)用了一個(gè)共同的方法 processRequest() ,,它是類在處理請(qǐng)求中最核心的方法。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //獲取 LocaleContextHolder 中原來(lái)保存的 LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); //獲取當(dāng)前請(qǐng)求的 LocaleContext LocaleContext localeContext = buildLocaleContext(request); //獲取 RequestContextHolder 中原來(lái)保存的 RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //獲取當(dāng)前請(qǐng)求的 ServletRequestAttributes ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());//將當(dāng)前請(qǐng)求的 LocaleContext 和 ServletRequestAttributes 設(shè)置到 LocaleContextHolder 和 RequestContextHolder initContextHolders(request, localeContext, requestAttributes); try { //實(shí)際處理請(qǐng)求的入口,,這是一個(gè)模板方法,,在 Dispatcher 類中才有具體實(shí)現(xiàn) doService(request, response); }catch (ServletException ex) { failureCause = ex; throw ex; }catch (IOException ex) { failureCause = ex; throw ex; }catch (Throwable ex) { failureCause = ex; throw new NestedServletException('Request processing failed', ex); }finally { //將 previousLocaleContext,previousAttributes 恢復(fù)到 LocaleContextHolder 和 RequestContextHolder 中 resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } //刪除了日志打印代碼 //發(fā)布了一個(gè) ServletRequestHandledEvent 類型的消息 publishRequestHandledEvent(request, response, startTime, failureCause); } }

DispatcherServlet

上一章中其實(shí)還沒(méi)把該類講清楚,,在這個(gè)類中,,里面的智行處理的入口方法應(yīng)該是 doService 方法,方法里面調(diào)用了 doDispatch 進(jìn)行具體的處理,,在調(diào)用 doDispatch 方法之前 doService 做了一些事情:首先判斷是不是 include 請(qǐng)求,,如果是則對(duì) request 的 Attribute 做個(gè)快照備份,等 doDispatcher 處理完之后(如果不是異步調(diào)用且未完成)進(jìn)行還原 ,,在做完快照后又對(duì) request 設(shè)置了一些屬性,。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)){ attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { //調(diào)用 doDispatch 方法 doDispatch(request, response); }finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }

doDispatch() 方法:

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 { //檢查是不是上傳請(qǐng)求 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. 根據(jù) request 找到 Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request.根據(jù) Handler 找到對(duì)應(yīng)的 HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. //處理 GET 、 HEAD 請(qǐng)求的 LastModified String method = request.getMethod(); boolean isGet = 'GET'.equals(method); if (isGet || 'HEAD'.equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug('Last-Modified value for [' + getRequestUri(request) + '] is: ' + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //執(zhí)行相應(yīng)的 Interceptor 的 preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. HandlerAdapter 使用 Handler 處理請(qǐng)求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //如果需要異步處理,,直接返回 if (asyncManager.isConcurrentHandlingStarted()) { return; } //當(dāng) view 為空時(shí),,根據(jù) request 設(shè)置默認(rèn) view applyDefaultViewName(processedRequest, mv); //執(zhí)行相應(yīng) Interceptor 的 postHandler 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); } //調(diào)用 processDispatchResult 方法處理上面處理之后的結(jié)果(包括處理異常,渲染頁(yè)面,,發(fā)出完成通知觸發(fā) Interceptor 的 afterCompletion) 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 { //判斷是否執(zhí)行異步請(qǐng)求 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. 刪除上傳請(qǐng)求的資源 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }

Handler,,HandlerMapping,HandlerAdapter 三個(gè)區(qū)別:

  • Handler:處理器,,對(duì)應(yīng) MVC 的 C層,,也就是 Controller 層,具體表現(xiàn)形式有很多種,,可以是類,,方法,它的類型是 Object,,只要可以處理實(shí)際請(qǐng)求就可以是 Handler,。

  • HandlerMapping:用來(lái)查找 Handler 的。

  • HandlerAdapter :Handler 適配器,

另外 View 和 ViewResolver 的原理與 Handler 和 HandlerMapping 的原理類似,。

看透 Spring MVC 源代碼分析與實(shí)踐——俯視 Spring MVC

doDispatcher

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多