源:https://www.cnblogs.com/youzhibing/p/10695012.html
作者:youzhibing2904
遺留問題
在關于利用maven搭建ssm的博客,我們一起來探討下問的最多的問題中,我遺留了一個問題:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域,這里的作用域指的是Servlet的四大作用域;不了解問題背景的可以回過頭去看看我的上篇博文。
明確的解答我會放到最后,在解答問題之前,我先和大家一起來捋一捋Spring mvc的工作原理。廢話不多說,開始我們神秘的探險之旅!
在講工作原理之前,我們先看一個簡單的spring mvc(ssm)示例,以及實現的效果
工程代碼地址:ssm-web
工程結構與效果如上所示,我們不做過多的探究,我們打起精神往下看本篇的重點
準備 - 資源的加載與初始化
1、DispatcherServlet 靜態初始化
DispatcherServlet中有如下靜態塊
static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. 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()); } }
這里會將DispatcherServlet.properties中的內容讀取到DispatcherServlet的屬性:private static final Properties defaultStrategies中,DispatcherServlet.properties內容如下
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
指定了DispatcherServlet策略接口的默認實現,后續DispatcherServlet初始化策略的時候會用到
2、interceptor定義的加載
spring啟動過程中會調用InterceptorsBeanDefinitionParser的parse方法來解析出我們自定義的interceptor定義,封裝成MappedInterceptor類型的bean定義,并放到spring容器中;我們可以簡單的認為spring容器中已經存在了我們自定義的interceptor的bean定義
3、DispatcherServlet初始化策略:initStrategies
DispatcherServlet的繼承圖如下
DispatcherServlet是一個Servlet,tomcat啟動過程中會調用其init方法,一串的調用后,會調用DispatcherServlet的initStrategies方法
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
實例化步驟1中的默認實現,并填充到DispatcherServlet各個屬性值中
4、DefaultAnnotationHandlerMapping的攔截器初始化
DispatcherServlet.properties種指定了兩個默認的HandlerMapping:BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping,這兩者的類繼承圖如下(我們暫時只關注DefaultAnnotationHandlerMapping)
DefaultAnnotationHandlerMapping間接實現了ApplicationContextAware,那么在DefaultAnnotationHandlerMapping實例初始化過程中,會調用setApplicationContext(ApplicationContext applicationContext)方法,一串調用后,會來到AbstractUrlHandlerMapping的initApplicationContext()
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }
初始化了DefaultAnnotationHandlerMapping的攔截器:interceptor
我們來看下具體的初始化過程,看看上面的順序是否只是我個人的臆想?
可以看到,初始化順序就是我們上面說的,不是我個人的意淫;此時的DefaultAnnotationHandlerMapping中有我們自定義的MyInterceptor。初始化過程我們需要關注的就是上述這些,下面我們一起看看具體請求的過程
請求的處理
請求從servlet的service開始,一路到DispatcherServlet的doDispatch,如下圖
/** * Process the actual dispatching to the handler. 將請求分發到具體的handler,也就是我們的controller * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ 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; // Determine handler for the current request. 決定哪個handler來處理當前的請求 // mappedHandler是由handler和interceptor集合組成的一個執行鏈,有點類似FilterChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 決定哪個adapter來處理當前的請求 // handlerMapping是找出適配的handler,而真正回調handler的是adapter 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 (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // handler的前置處理,也就是調用適配當前url的interceptor的preHandler方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler. 真正調用handler mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); // handler的后置處理,也就是調用適配當前url的interceptor的postHandler方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } // 處理handler返回的結果,會調用適配當前url的interceptor的afterCompletion方法 // 這里會將響應結果返回給請求者 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }
handlerMapping具體如何找到匹配當前url的handler(一般而言就是我們的controller)、handlerAdapter具體如何回調真正的handler,有興趣的可以自行去跟下,我就不跟了。我們具體看下processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 這個與我們最初的疑問有關
processDispatchResult
可以看到model中的persons會被設置到request的attributes中,然后轉發請求到show_person.jsp,轉發過程中request作用域的變量仍然有效,所以show_person.jsp中的jstl標簽和el表達式能夠取到persons變量,最后將show_person.jsp中的內容填充好之后的靜態內容返回給請求者;至此就完成了一次請求的響應
回到我們開篇的疑問:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域?想必大家已經知道答案了
Controller中的model、ModelMap的注入由spring mvc完成,這個不是請求傳入的參數,用于綁定變量到Servlet作用域;默認情況下,在DispatcherServlet調用了真正的handler之后,將結果返回給請求者的過程中,將model、modelMap中的變量設置到了request的attributes中,轉發的過程中,request中的變量仍然有效,所以show_person.jsp中能取到persons這個變量,自此疑問得到解答
1、Spring MVC工作原理圖
圖是用的別人的,具體是誰的我也不記得了(捂臉)
2、DefaultAnnotationHandlerMapping在spring3.2中被廢棄,替換成了RequestMappingHandlerMapping
對于 Web 應用程序而言,我們從瀏覽器發起一個請求,請求經過一系列的分發和處理,最終會進入到我們指定的方法之中,這一系列的的具體流程到底是怎么樣的呢?
記得在初入職場的時候,面試前經常會背一背 Spring MVC 流程,印象最深的就是一個請求最先會經過 DispatcherServlet 進行分發處理,DispatcherServlet 就是我們 Spring MVC 的入口類,下面就是一個請求的大致流轉流程(圖片參考自 Spring In Action):
上面就是一個傳統的完整的 Spring MVC 流程,為什么要說這是傳統的流程呢?因為這個流程是用于前后端沒有分離的時候,后臺直接返回頁面給瀏覽器進行渲染,而現在大部分應用都是前后端分離,后臺直接生成一個 Json 字符串就直接返回前端,不需要經過視圖解析器進行處理,也就是說前后端分離之后,流程就簡化成了 1-2-3-4-7(其中第四步返回的一般是 Json 格式數據)。
Spring MVC主要可以分為兩大過程,一是初始化,二就是處理請求。初始化的過程主要就是將我們定義好的 RequestMapping 映射路徑和 Controller 中的方法進行一一映射存儲,這樣當收到請求之后就可以處理請求調用對應的方法,從而響應請求。
初始化過程的入口方法是 DispatchServlet 的 init() 方法,而實際上 DispatchServlet 中并沒有這個方法,所以我們就繼續尋找父類,會發現 init 方法在其父類(FrameworkServlet)的父類 HttpServletBean 中。
在這個方法中,首先會去家在一些 Servlet 相關配置(web.xml),然后會調用 initServletBean() 方法,這個方法是一個空的模板方法,業務邏輯由子類 FrameworkServlet 來實現。
這個方法本身沒有什么業務邏輯,主要是初始化 WebApplicationContext 對象,WebApplicationContext 繼承自 ApplicationContext,主要是用來處理 web 應用的上下文。
initWebApplicationContext() 方法主要就是為了找到一個上下文,找不到就會創建一個上下文,創建之后,最終會調用方法 configureAndRefreshWebApplicationContext(cwac) 方法,而這個方法最終在設置一些基本容器標識信息之后會去調用 refresh()方法,也就是初始化 ioc 容器。
當調用 refresh() 方法初始化 ioc 容器之后,最終會調用方法 onRefresh(),這個方法也是一個模板鉤子方法,由子類實現,也就是回到了我們 Spring MVC 的入口類 DispatcherServlet。
onRefresh() 方法就是 Spring MVC 初始化的最后一個步驟,在這個步驟當中會初始化 Spring MVC 流程中可能需要使用到的九大組件。
這個組件比較熟悉,主要就是用來處理文件上傳請求,通過將普通的 Request 對象包裝成 MultipartHttpServletRequest 對象來進行處理。
LocaleResolver 用于初始化本地語言環境,其從 Request 對象中解析出當前所處的語言環境,如中國大陸則會解析出 zh-CN 等等,模板解析以及國際化的時候都會用到本地語言環境。
這個主要是用戶主題解析,在 Spring MVC 中,一套主題對應一個 .properties 文件,可以存放和當前主題相關的所有資源,如圖片,css樣式等。
用于查找處理器(Handler),比如我們 Controller 中的方法,這個其實最主要就是用來存儲 url 和 調用方法的映射關系,存儲好映射關系之后,后續有請求進來,就可以知道調用哪個 Controller 中的哪個方法,以及方法的參數是哪些。
這是一個適配器,因為 Spring MVC 中支持很多種 Handler,但是最終將請求交給 Servlet 時,只能是 doService(req,resp) 形式,所以 HandlerAdapter 就是用來適配轉換格式的。
這個組件主要是用來處理異常,不過看名字也很明顯,這個只會對處理 Handler 時產生的異常進行處理,然后會根據異常設置對應的 ModelAndView,然后交給 Render 渲染成頁面。
這個主鍵主要是從 Request 中獲取到視圖名稱。
這個組件會依賴于 RequestToViewNameTranslator 組件獲取到的視圖名稱,因為視圖名稱是字符串格式,所以這里會將字符串格式的視圖名稱轉換成為 View 類型視圖,最終經過一系列解析和變量替換等操作返回一個頁面到前端。
這個主鍵主要是用來管理 FlashMap,那么 FlashMap 又有什么用呢?要明白這個那就不得不提到重定向了,有時候我們提交一個請求的時候會需要重定向,那么假如參數過多或者說我們不想把參數拼接到 url 上(比如敏感數據之類的),這時候怎么辦呢?因為參數不拼接在 url 上重定向是無法攜帶參數的。
FlashMap 就是為了解決這個問題,我們可以在請求發生重定向之前,將參數寫入 request 的屬性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,這樣在重定向之后的 handler 中,Spring 會自動將其設置到 Model 中,這樣就可以從 Model 中取到我們傳遞的參數了。
在九大組件初始化完成之后,Spring MVC 的初始化就完成了,接下來就是接收并處理請求了,那么處理請求的入口在哪里呢?處理請求的入口方法就是 DispatcherServlet 中的 doService 方法,而 doService 方法又會調用 doDispatch 方法。
這個方法最關鍵的就是調用了 getHandler 方法,這個方法就是會獲取到前面九大組件中的 HandlerMapping,然后進行反射調用對應的方法完成請求,完成請求之后后續還會經過視圖轉換之類的一些操作,最終返回 ModelAndView,不過現在都是前后端分離,基本也不需要用到視圖模型,在這里我們就不分析后續過程,主要就是分析 HandlerMapping 的初始化和查詢過程。
這個方法里面會遍歷 handllerMappings,這個 handllerMappings 是一個 List 集合,因為 HandlerMapping 有多重實現,也就是 HandlerMapping 不止一個實現,其最常用的兩個實現為 RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。
AbstractHandlerMapping 是一個抽象類,其 getHandlerInternal 這個方法也是一個模板方法:
getHandlerInternal 方法最終其會調用子類實現,而這里的子類實現會有多個,其中最主要的就是 AbstractHandlerMethodMapping 和 AbstractUrlHandlerMapping 兩個抽象類,那么最終到底會調用哪個實現類呢?
這時候如果拿捏不準我們就可以看一下類圖,上面我們提到,HandlerMapper 有兩個非常主要的實現類:RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。那么我們就分別來看一下這兩個類的類圖關系:
可以看到,這兩個實現類的抽象父類正好對應了 AbstractHandlerMapping 的兩個子類,所以這時候具體看哪個方法,那就看我們想看哪種類型了。
其實除了這兩種 HandlerMapping 之外,Spring 中還有其他一些 HandllerMapping,如 SimpleUrlHandlerMapping 等。
提到的這幾種 HandlerMapping,對我們來說最常用,最熟悉的那肯定就是 RequestMappingHandlerMapping ,在這里我們就以這個為例來進行分析,所以我們應該
這個方法本身也沒有什么邏輯,其主要的核心查找 Handler 邏輯在 lookupHandlerMethod 方法中,這個方法主要是為了獲取一個 HandlerMethod 對象,前面的方法都是 Object,而到這里變成了 HandlerMethod 類型,這是因為 Handler 有各種類型,目前我們已經基本跟到了具體類型之下,所以類型就變成了具體類型,而如果我們看的的另一條分支線,那么返回的就會是其他對象,正是因為支持多種不同類型的 HandlerMapping 對象,所以最終為了統一執行,才會需要在獲得 Hanlder 之后,DispatcherServlet 中會再次通過調用 getHandlerAdapter 方法來進一步封裝成 HandlerAdapter 對象,才能進行方法的調用
這個方法主要會從 mappingRegistry 中獲取命中的方法,獲取之后還會經過一系列的判斷比較判斷比較,因為有些 url 會對應多個方法,而方法的請求類型不同,比如一個 GET 方法,一個 POST 方法,或者其他一些屬性不相同等等,都會導致最終命中到不同的方法,這些邏輯主要都是在 addMatchingMappings 方法去進一步實現,并最終將命中的結果加入到 matches 集合內。
在這個方法中,有一個對象非常關鍵,那就是 mappingRegistry,因為最終我們根據 url 到這里獲取到對應的 HandlerMtthod,所以這個對象很關鍵:
看這個對象其實很明顯可以看出來,這個對象其實只是維護了一些 Map 對象,所以我們可以很容易猜測到,一定在某一個地方,將 url 和 HandlerMapping 或者 HandlerMethod 的映射關系存進來了,這時候其實我們可以根據 getMappingsByUrl 方法來進行反推,看看 urlLookup 這個 Map 是什么時候被存入的,結合上面的類圖關系,一路反推,很容易就可以找到這個 Map 中的映射關系是 AbstractHandlerMethodMapping 對象的 afterPropertiesSet 方法實現的(AbstractHandlerMethodMapping 實現了 InitializingBean 接口),也就是當這個對象初始化完成之后,我們的 url 和 Handler 映射關系已經存入了 MappingRegistry 對象中的集合 Map 中。
afterPropertiesSet 方法中并沒有任何邏輯,而是直接調用了 initHandlerMethods。
initHandlerMethods 方法中,首先還是會從 Spring 的上下文中獲取所有的 Bean,然后會進一步從帶有 RequestMapping 注解和 Controller 注解中的 Bean 去解析并獲得 HandlerMethod。
這個方法中,其實就是通過反射獲取到 Controller 中的所有方法,然后調用 registerHandlerMethod 方法將相關信息注冊到 MappingRegistry 對象中的各種 Map 集合之內:
registerHandlerMethod 方法中會直接調用 AbstractHandlerMethodMapping 對象持有的 mappingRegistry 對象中的 regidter方法,這里會對 Controller 中方法上的一些元信息進行各種解析,比如參數,路徑,請求方式等等,然后會將各種信息注冊到對應的 Map 集合中,最終完成了整個初始化。
本文重點以 RequestMappingHandlerMapping 為例子分析了在 Spring 當中如何初始化 HandlerMethod,并最終在調用的時候又是如何根據 url 獲取到對應的方法并進行執行最終完成整個流程。
模型-視圖-控制器(MVC 是一個眾所周知的以設計界面應用程序為基礎的設計模式。它主要通過分離模型、視圖及控制器在應用程序中的角色將業務邏輯從界面中解耦。通常,模型負責封裝應用程序數據在視圖層展示。視圖僅僅只是展示這些數據,不包含任何業務邏輯。控制器負責接收來自用戶的請求,并調用后臺服務(manager或者dao)來處理業務邏輯。處理后,后臺業務層可能會返回了一些數據在視圖層展示??刂破魇占@些數據及準備模型在視圖層展示。MVC模式的核心思想是將業務邏輯從界面中分離出來,允許它們單獨改變而不會相互影響。
1.Spring Web MVC是一種基于Java的實現了Web MVC設計模式的請求驅動類型的輕量級Web框架
2.使用了MVC架構模式的思想,將web層進行職責解耦
3.基于請求驅動指的就是使用請求-響應模型
4.框架的目的就是幫助我們簡化開發,
Spring Web MVC也是要簡化我們日常Web開發的。
1.性能比struts2好
2.簡單、便捷,易學
3.和spring無縫銜接【IOC,AOP】
4.使用約定優于配置
5.支持Restful
6.異常處理,國際化,數據驗證,類型轉換等
7.使用的人多,使用的公司多
普通web項目
在src目錄下創建一個 spring-mvc.xml文件,名稱可以自定義。內容就是spring的schema內容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
1234567
在spring-mvc.xml中添加
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 處理器映射器 將bean的name作為url進行查找 ,
需要在配置Handler時指定beanname(就是url) 所有的映射器都實現
HandlerMapping接口。
-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 配置 Controller適配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
</beans>
123456789101112131415
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>test</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置前端控制器 -->
<!-- contextConfigLocation配置springmvc加載的配置文件(配置處理器映射器、適配器等等)
如果不配置contextConfigLocation,
默認加載的是/WEB-INF/servlet名稱-serlvet.xml(springmvc-servlet.xml)
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
12345678910111213141516171819202122232425262728293031
/**
* 自定義控制器
* 必須實現Controller接口
* @author dpb【波波烤鴨】
*
*/
public class UserController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("本方法被調用了...");
ModelAndView view = new ModelAndView();
view.setViewName("/index.jsp");
return view;
}
}
12345678910111213141516
通過上一個普通實現的方式大家會發現其實現步驟比較繁瑣,而且自定義controller也只有一個默認被調用的方法。不是很方便,這時我們可以使用SpringMVC基于注解的使用方式來實現,步驟如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 開啟注解 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 開啟掃描 -->
<context:component-scan base-package="com.dpb.controller"></context:component-scan>
</beans>
12345678910111213
/**
* 自定義controller
* @author dpb【波波烤鴨】
*
*/
@Controller // 交給Spring容器管理
@RequestMapping("/user") // 設置請求的路徑
public class UserController {
/**
* 查詢方法
* 請求地址是:
* http://localhost:8080/SpringMVC-03-hellowordAnnation/user/query
* @return
*/
@RequestMapping("/query")
public ModelAndView query(){
System.out.println("波波烤鴨:query");
ModelAndView mv = new ModelAndView();
mv.setViewName("/index.jsp");
return mv;
}
/**
* 添加方法
* 請求地址是:
* http://localhost:8080/SpringMVC-03-hellowordAnnation/user/add
* @return
*/
@RequestMapping("/add")
public ModelAndView add(){
System.out.println("波波烤鴨:add");
ModelAndView mv = new ModelAndView();
mv.setViewName("/index.jsp");
return mv;
}
}
12345678910111213141516171819202122232425262728293031323334353637
1.用戶向服務器發送請求,請求被Spring 前端控制Servelt DispatcherServlet捕獲;
2.DispatcherServlet對請求URL進行解析,得到請求資源標識符(URI)。然后根據該URI,調用HandlerMapping獲得該Handler配置的所有相關的對象(包括Handler對象以及Handler對象對應的攔截器),最后以HandlerExecutionChain對象的形式返回;
3.DispatcherServlet 根據獲得的Handler,選擇一個合適的HandlerAdapter。(附注:如果成功獲得HandlerAdapter后,此時將開始執行攔截器的preHandler(…)方法)
4.提取Request中的模型數據,填充Handler入參,開始執行Handler(Controller)。 在填充Handler的入參過程中,根據你的配置,Spring將幫你做一些額外的工作:
HttpMessageConveter: 將請求消息(如Json、xml等數據)轉換成一個對象,將對象轉換為指定的響應信息
數據轉換:對請求消息進行數據轉換。如String轉換成Integer、Double等
數據格式化:對請求消息進行數據格式化。 如將字符串轉換成格式化數字或格式化日期等
數據驗證: 驗證數據的有效性(長度、格式等),驗證結果存儲到BindingResult或Error中
5.Handler執行完成后,向DispatcherServlet 返回一個ModelAndView對象;
6.根據返回的ModelAndView,選擇一個適合的ViewResolver(必須是已經注冊到Spring容器中的ViewResolver)返回給DispatcherServlet ;
7.ViewResolver 結合Model和View,來渲染視圖
8.將渲染結果返回給客戶端。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。