Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
源:https://www.cnblogs.com/youzhibing/p/10695012.html
作者:youzhibing2904
遺留問題
在關(guān)于利用maven搭建ssm的博客,我們一起來探討下問的最多的問題中,我遺留了一個問題:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域,這里的作用域指的是Servlet的四大作用域;不了解問題背景的可以回過頭去看看我的上篇博文。
明確的解答我會放到最后,在解答問題之前,我先和大家一起來捋一捋Spring mvc的工作原理。廢話不多說,開始我們神秘的探險之旅!
在講工作原理之前,我們先看一個簡單的spring mvc(ssm)示例,以及實現(xiàn)的效果
工程代碼地址:ssm-web
工程結(jié)構(gòu)與效果如上所示,我們不做過多的探究,我們打起精神往下看本篇的重點
準備 - 資源的加載與初始化
1、DispatcherServlet 靜態(tài)初始化
DispatcherServlet中有如下靜態(tài)塊
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中的內(nèi)容讀取到DispatcherServlet的屬性:private static final Properties defaultStrategies中,DispatcherServlet.properties內(nèi)容如下
# 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策略接口的默認實現(xiàn),后續(xù)DispatcherServlet初始化策略的時候會用到
2、interceptor定義的加載
spring啟動過程中會調(diào)用InterceptorsBeanDefinitionParser的parse方法來解析出我們自定義的interceptor定義,封裝成MappedInterceptor類型的bean定義,并放到spring容器中;我們可以簡單的認為spring容器中已經(jīng)存在了我們自定義的interceptor的bean定義
3、DispatcherServlet初始化策略:initStrategies
DispatcherServlet的繼承圖如下
DispatcherServlet是一個Servlet,tomcat啟動過程中會調(diào)用其init方法,一串的調(diào)用后,會調(diào)用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中的默認實現(xiàn),并填充到DispatcherServlet各個屬性值中
4、DefaultAnnotationHandlerMapping的攔截器初始化
DispatcherServlet.properties種指定了兩個默認的HandlerMapping:BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping,這兩者的類繼承圖如下(我們暫時只關(guān)注DefaultAnnotationHandlerMapping)
DefaultAnnotationHandlerMapping間接實現(xiàn)了ApplicationContextAware,那么在DefaultAnnotationHandlerMapping實例初始化過程中,會調(diào)用setApplicationContext(ApplicationContext applicationContext)方法,一串調(diào)用后,會來到AbstractUrlHandlerMapping的initApplicationContext()
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }
初始化了DefaultAnnotationHandlerMapping的攔截器:interceptor
我們來看下具體的初始化過程,看看上面的順序是否只是我個人的臆想?
可以看到,初始化順序就是我們上面說的,不是我個人的意淫;此時的DefaultAnnotationHandlerMapping中有我們自定義的MyInterceptor。初始化過程我們需要關(guān)注的就是上述這些,下面我們一起看看具體請求的過程
請求的處理
請求從servlet的service開始,一路到DispatcherServlet的doDispatch,如下圖
/** * Process the actual dispatching to the handler. 將請求分發(fā)到具體的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集合組成的一個執(zhí)行鏈,有點類似FilterChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 決定哪個adapter來處理當前的請求 // handlerMapping是找出適配的handler,而真正回調(diào)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的前置處理,也就是調(diào)用適配當前url的interceptor的preHandler方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler. 真正調(diào)用handler mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); // handler的后置處理,也就是調(diào)用適配當前url的interceptor的postHandler方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } // 處理handler返回的結(jié)果,會調(diào)用適配當前url的interceptor的afterCompletion方法 // 這里會將響應(yīng)結(jié)果返回給請求者 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具體如何回調(diào)真正的handler,有興趣的可以自行去跟下,我就不跟了。我們具體看下processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 這個與我們最初的疑問有關(guān)
processDispatchResult
可以看到model中的persons會被設(shè)置到request的attributes中,然后轉(zhuǎn)發(fā)請求到show_person.jsp,轉(zhuǎn)發(fā)過程中request作用域的變量仍然有效,所以show_person.jsp中的jstl標簽和el表達式能夠取到persons變量,最后將show_person.jsp中的內(nèi)容填充好之后的靜態(tài)內(nèi)容返回給請求者;至此就完成了一次請求的響應(yīng)
回到我們開篇的疑問:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域?想必大家已經(jīng)知道答案了
Controller中的model、ModelMap的注入由spring mvc完成,這個不是請求傳入的參數(shù),用于綁定變量到Servlet作用域;默認情況下,在DispatcherServlet調(diào)用了真正的handler之后,將結(jié)果返回給請求者的過程中,將model、modelMap中的變量設(shè)置到了request的attributes中,轉(zhuǎn)發(fā)的過程中,request中的變量仍然有效,所以show_person.jsp中能取到persons這個變量,自此疑問得到解答
1、Spring MVC工作原理圖
圖是用的別人的,具體是誰的我也不記得了(捂臉)
2、DefaultAnnotationHandlerMapping在spring3.2中被廢棄,替換成了RequestMappingHandlerMapping
對于 Web 應(yīng)用程序而言,我們從瀏覽器發(fā)起一個請求,請求經(jīng)過一系列的分發(fā)和處理,最終會進入到我們指定的方法之中,這一系列的的具體流程到底是怎么樣的呢?
記得在初入職場的時候,面試前經(jīng)常會背一背 Spring MVC 流程,印象最深的就是一個請求最先會經(jīng)過 DispatcherServlet 進行分發(fā)處理,DispatcherServlet 就是我們 Spring MVC 的入口類,下面就是一個請求的大致流轉(zhuǎn)流程(圖片參考自 Spring In Action):
上面就是一個傳統(tǒng)的完整的 Spring MVC 流程,為什么要說這是傳統(tǒng)的流程呢?因為這個流程是用于前后端沒有分離的時候,后臺直接返回頁面給瀏覽器進行渲染,而現(xiàn)在大部分應(yīng)用都是前后端分離,后臺直接生成一個 Json 字符串就直接返回前端,不需要經(jīng)過視圖解析器進行處理,也就是說前后端分離之后,流程就簡化成了 1-2-3-4-7(其中第四步返回的一般是 Json 格式數(shù)據(jù))。
Spring MVC主要可以分為兩大過程,一是初始化,二就是處理請求。初始化的過程主要就是將我們定義好的 RequestMapping 映射路徑和 Controller 中的方法進行一一映射存儲,這樣當收到請求之后就可以處理請求調(diào)用對應(yīng)的方法,從而響應(yīng)請求。
初始化過程的入口方法是 DispatchServlet 的 init() 方法,而實際上 DispatchServlet 中并沒有這個方法,所以我們就繼續(xù)尋找父類,會發(fā)現(xiàn) init 方法在其父類(FrameworkServlet)的父類 HttpServletBean 中。
在這個方法中,首先會去家在一些 Servlet 相關(guān)配置(web.xml),然后會調(diào)用 initServletBean() 方法,這個方法是一個空的模板方法,業(yè)務(wù)邏輯由子類 FrameworkServlet 來實現(xiàn)。
這個方法本身沒有什么業(yè)務(wù)邏輯,主要是初始化 WebApplicationContext 對象,WebApplicationContext 繼承自 ApplicationContext,主要是用來處理 web 應(yīng)用的上下文。
initWebApplicationContext() 方法主要就是為了找到一個上下文,找不到就會創(chuàng)建一個上下文,創(chuàng)建之后,最終會調(diào)用方法 configureAndRefreshWebApplicationContext(cwac) 方法,而這個方法最終在設(shè)置一些基本容器標識信息之后會去調(diào)用 refresh()方法,也就是初始化 ioc 容器。
當調(diào)用 refresh() 方法初始化 ioc 容器之后,最終會調(diào)用方法 onRefresh(),這個方法也是一個模板鉤子方法,由子類實現(xiàn),也就是回到了我們 Spring MVC 的入口類 DispatcherServlet。
onRefresh() 方法就是 Spring MVC 初始化的最后一個步驟,在這個步驟當中會初始化 Spring MVC 流程中可能需要使用到的九大組件。
這個組件比較熟悉,主要就是用來處理文件上傳請求,通過將普通的 Request 對象包裝成 MultipartHttpServletRequest 對象來進行處理。
LocaleResolver 用于初始化本地語言環(huán)境,其從 Request 對象中解析出當前所處的語言環(huán)境,如中國大陸則會解析出 zh-CN 等等,模板解析以及國際化的時候都會用到本地語言環(huán)境。
這個主要是用戶主題解析,在 Spring MVC 中,一套主題對應(yīng)一個 .properties 文件,可以存放和當前主題相關(guān)的所有資源,如圖片,css樣式等。
用于查找處理器(Handler),比如我們 Controller 中的方法,這個其實最主要就是用來存儲 url 和 調(diào)用方法的映射關(guān)系,存儲好映射關(guān)系之后,后續(xù)有請求進來,就可以知道調(diào)用哪個 Controller 中的哪個方法,以及方法的參數(shù)是哪些。
這是一個適配器,因為 Spring MVC 中支持很多種 Handler,但是最終將請求交給 Servlet 時,只能是 doService(req,resp) 形式,所以 HandlerAdapter 就是用來適配轉(zhuǎn)換格式的。
這個組件主要是用來處理異常,不過看名字也很明顯,這個只會對處理 Handler 時產(chǎn)生的異常進行處理,然后會根據(jù)異常設(shè)置對應(yīng)的 ModelAndView,然后交給 Render 渲染成頁面。
這個主鍵主要是從 Request 中獲取到視圖名稱。
這個組件會依賴于 RequestToViewNameTranslator 組件獲取到的視圖名稱,因為視圖名稱是字符串格式,所以這里會將字符串格式的視圖名稱轉(zhuǎn)換成為 View 類型視圖,最終經(jīng)過一系列解析和變量替換等操作返回一個頁面到前端。
這個主鍵主要是用來管理 FlashMap,那么 FlashMap 又有什么用呢?要明白這個那就不得不提到重定向了,有時候我們提交一個請求的時候會需要重定向,那么假如參數(shù)過多或者說我們不想把參數(shù)拼接到 url 上(比如敏感數(shù)據(jù)之類的),這時候怎么辦呢?因為參數(shù)不拼接在 url 上重定向是無法攜帶參數(shù)的。
FlashMap 就是為了解決這個問題,我們可以在請求發(fā)生重定向之前,將參數(shù)寫入 request 的屬性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,這樣在重定向之后的 handler 中,Spring 會自動將其設(shè)置到 Model 中,這樣就可以從 Model 中取到我們傳遞的參數(shù)了。
在九大組件初始化完成之后,Spring MVC 的初始化就完成了,接下來就是接收并處理請求了,那么處理請求的入口在哪里呢?處理請求的入口方法就是 DispatcherServlet 中的 doService 方法,而 doService 方法又會調(diào)用 doDispatch 方法。
這個方法最關(guān)鍵的就是調(diào)用了 getHandler 方法,這個方法就是會獲取到前面九大組件中的 HandlerMapping,然后進行反射調(diào)用對應(yīng)的方法完成請求,完成請求之后后續(xù)還會經(jīng)過視圖轉(zhuǎn)換之類的一些操作,最終返回 ModelAndView,不過現(xiàn)在都是前后端分離,基本也不需要用到視圖模型,在這里我們就不分析后續(xù)過程,主要就是分析 HandlerMapping 的初始化和查詢過程。
這個方法里面會遍歷 handllerMappings,這個 handllerMappings 是一個 List 集合,因為 HandlerMapping 有多重實現(xiàn),也就是 HandlerMapping 不止一個實現(xiàn),其最常用的兩個實現(xiàn)為 RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。
AbstractHandlerMapping 是一個抽象類,其 getHandlerInternal 這個方法也是一個模板方法:
getHandlerInternal 方法最終其會調(diào)用子類實現(xiàn),而這里的子類實現(xiàn)會有多個,其中最主要的就是 AbstractHandlerMethodMapping 和 AbstractUrlHandlerMapping 兩個抽象類,那么最終到底會調(diào)用哪個實現(xiàn)類呢?
這時候如果拿捏不準我們就可以看一下類圖,上面我們提到,HandlerMapper 有兩個非常主要的實現(xiàn)類:RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。那么我們就分別來看一下這兩個類的類圖關(guān)系:
可以看到,這兩個實現(xiàn)類的抽象父類正好對應(yīng)了 AbstractHandlerMapping 的兩個子類,所以這時候具體看哪個方法,那就看我們想看哪種類型了。
其實除了這兩種 HandlerMapping 之外,Spring 中還有其他一些 HandllerMapping,如 SimpleUrlHandlerMapping 等。
提到的這幾種 HandlerMapping,對我們來說最常用,最熟悉的那肯定就是 RequestMappingHandlerMapping ,在這里我們就以這個為例來進行分析,所以我們應(yīng)該
這個方法本身也沒有什么邏輯,其主要的核心查找 Handler 邏輯在 lookupHandlerMethod 方法中,這個方法主要是為了獲取一個 HandlerMethod 對象,前面的方法都是 Object,而到這里變成了 HandlerMethod 類型,這是因為 Handler 有各種類型,目前我們已經(jīng)基本跟到了具體類型之下,所以類型就變成了具體類型,而如果我們看的的另一條分支線,那么返回的就會是其他對象,正是因為支持多種不同類型的 HandlerMapping 對象,所以最終為了統(tǒng)一執(zhí)行,才會需要在獲得 Hanlder 之后,DispatcherServlet 中會再次通過調(diào)用 getHandlerAdapter 方法來進一步封裝成 HandlerAdapter 對象,才能進行方法的調(diào)用
這個方法主要會從 mappingRegistry 中獲取命中的方法,獲取之后還會經(jīng)過一系列的判斷比較判斷比較,因為有些 url 會對應(yīng)多個方法,而方法的請求類型不同,比如一個 GET 方法,一個 POST 方法,或者其他一些屬性不相同等等,都會導(dǎo)致最終命中到不同的方法,這些邏輯主要都是在 addMatchingMappings 方法去進一步實現(xiàn),并最終將命中的結(jié)果加入到 matches 集合內(nèi)。
在這個方法中,有一個對象非常關(guān)鍵,那就是 mappingRegistry,因為最終我們根據(jù) url 到這里獲取到對應(yīng)的 HandlerMtthod,所以這個對象很關(guān)鍵:
看這個對象其實很明顯可以看出來,這個對象其實只是維護了一些 Map 對象,所以我們可以很容易猜測到,一定在某一個地方,將 url 和 HandlerMapping 或者 HandlerMethod 的映射關(guān)系存進來了,這時候其實我們可以根據(jù) getMappingsByUrl 方法來進行反推,看看 urlLookup 這個 Map 是什么時候被存入的,結(jié)合上面的類圖關(guān)系,一路反推,很容易就可以找到這個 Map 中的映射關(guān)系是 AbstractHandlerMethodMapping 對象的 afterPropertiesSet 方法實現(xiàn)的(AbstractHandlerMethodMapping 實現(xiàn)了 InitializingBean 接口),也就是當這個對象初始化完成之后,我們的 url 和 Handler 映射關(guān)系已經(jīng)存入了 MappingRegistry 對象中的集合 Map 中。
afterPropertiesSet 方法中并沒有任何邏輯,而是直接調(diào)用了 initHandlerMethods。
initHandlerMethods 方法中,首先還是會從 Spring 的上下文中獲取所有的 Bean,然后會進一步從帶有 RequestMapping 注解和 Controller 注解中的 Bean 去解析并獲得 HandlerMethod。
這個方法中,其實就是通過反射獲取到 Controller 中的所有方法,然后調(diào)用 registerHandlerMethod 方法將相關(guān)信息注冊到 MappingRegistry 對象中的各種 Map 集合之內(nèi):
registerHandlerMethod 方法中會直接調(diào)用 AbstractHandlerMethodMapping 對象持有的 mappingRegistry 對象中的 regidter方法,這里會對 Controller 中方法上的一些元信息進行各種解析,比如參數(shù),路徑,請求方式等等,然后會將各種信息注冊到對應(yīng)的 Map 集合中,最終完成了整個初始化。
本文重點以 RequestMappingHandlerMapping 為例子分析了在 Spring 當中如何初始化 HandlerMethod,并最終在調(diào)用的時候又是如何根據(jù) url 獲取到對應(yīng)的方法并進行執(zhí)行最終完成整個流程。
模型-視圖-控制器(MVC 是一個眾所周知的以設(shè)計界面應(yīng)用程序為基礎(chǔ)的設(shè)計模式。它主要通過分離模型、視圖及控制器在應(yīng)用程序中的角色將業(yè)務(wù)邏輯從界面中解耦。通常,模型負責封裝應(yīng)用程序數(shù)據(jù)在視圖層展示。視圖僅僅只是展示這些數(shù)據(jù),不包含任何業(yè)務(wù)邏輯。控制器負責接收來自用戶的請求,并調(diào)用后臺服務(wù)(manager或者dao)來處理業(yè)務(wù)邏輯。處理后,后臺業(yè)務(wù)層可能會返回了一些數(shù)據(jù)在視圖層展示。控制器收集這些數(shù)據(jù)及準備模型在視圖層展示。MVC模式的核心思想是將業(yè)務(wù)邏輯從界面中分離出來,允許它們單獨改變而不會相互影響。
1.Spring Web MVC是一種基于Java的實現(xiàn)了Web MVC設(shè)計模式的請求驅(qū)動類型的輕量級Web框架
2.使用了MVC架構(gòu)模式的思想,將web層進行職責解耦
3.基于請求驅(qū)動指的就是使用請求-響應(yīng)模型
4.框架的目的就是幫助我們簡化開發(fā),
Spring Web MVC也是要簡化我們?nèi)粘eb開發(fā)的。
1.性能比struts2好
2.簡單、便捷,易學(xué)
3.和spring無縫銜接【IOC,AOP】
4.使用約定優(yōu)于配置
5.支持Restful
6.異常處理,國際化,數(shù)據(jù)驗證,類型轉(zhuǎn)換等
7.使用的人多,使用的公司多
普通web項目
在src目錄下創(chuàng)建一個 spring-mvc.xml文件,名稱可以自定義。內(nèi)容就是spring的schema內(nèi)容
<?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) 所有的映射器都實現(xiàn)
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
/**
* 自定義控制器
* 必須實現(xiàn)Controller接口
* @author dpb【波波烤鴨】
*
*/
public class UserController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("本方法被調(diào)用了...");
ModelAndView view = new ModelAndView();
view.setViewName("/index.jsp");
return view;
}
}
12345678910111213141516
通過上一個普通實現(xiàn)的方式大家會發(fā)現(xiàn)其實現(xiàn)步驟比較繁瑣,而且自定義controller也只有一個默認被調(diào)用的方法。不是很方便,這時我們可以使用SpringMVC基于注解的使用方式來實現(xiàn),步驟如下:
<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") // 設(shè)置請求的路徑
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.用戶向服務(wù)器發(fā)送請求,請求被Spring 前端控制Servelt DispatcherServlet捕獲;
2.DispatcherServlet對請求URL進行解析,得到請求資源標識符(URI)。然后根據(jù)該URI,調(diào)用HandlerMapping獲得該Handler配置的所有相關(guān)的對象(包括Handler對象以及Handler對象對應(yīng)的攔截器),最后以HandlerExecutionChain對象的形式返回;
3.DispatcherServlet 根據(jù)獲得的Handler,選擇一個合適的HandlerAdapter。(附注:如果成功獲得HandlerAdapter后,此時將開始執(zhí)行攔截器的preHandler(…)方法)
4.提取Request中的模型數(shù)據(jù),填充Handler入?yún)ⅲ_始執(zhí)行Handler(Controller)。 在填充Handler的入?yún)⑦^程中,根據(jù)你的配置,Spring將幫你做一些額外的工作:
HttpMessageConveter: 將請求消息(如Json、xml等數(shù)據(jù))轉(zhuǎn)換成一個對象,將對象轉(zhuǎn)換為指定的響應(yīng)信息
數(shù)據(jù)轉(zhuǎn)換:對請求消息進行數(shù)據(jù)轉(zhuǎn)換。如String轉(zhuǎn)換成Integer、Double等
數(shù)據(jù)格式化:對請求消息進行數(shù)據(jù)格式化。 如將字符串轉(zhuǎn)換成格式化數(shù)字或格式化日期等
數(shù)據(jù)驗證: 驗證數(shù)據(jù)的有效性(長度、格式等),驗證結(jié)果存儲到BindingResult或Error中
5.Handler執(zhí)行完成后,向DispatcherServlet 返回一個ModelAndView對象;
6.根據(jù)返回的ModelAndView,選擇一個適合的ViewResolver(必須是已經(jīng)注冊到Spring容器中的ViewResolver)返回給DispatcherServlet ;
7.ViewResolver 結(jié)合Model和View,來渲染視圖
8.將渲染結(jié)果返回給客戶端。
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。