Spring MVC總結(jié)

1.Spring背景

1.1.Spring四大原則:

  1. 使用POJO進(jìn)行輕量級(jí)和最侵入式開發(fā);
  2. 通過(guò)依賴注入和基于借口編程實(shí)現(xiàn)松耦合;
  3. 通過(guò)AOP和默認(rèn)習(xí)慣進(jìn)行聲明式編程萌业;
  4. 使用AOP和模板(template)減少模塊化代碼
    Spring所有功能的設(shè)計(jì)和實(shí)現(xiàn)都是基于這四大原則的稚虎。

1.2.依賴注入

容器負(fù)責(zé)創(chuàng)建對(duì)象和維護(hù)對(duì)象之間的依賴關(guān)系侧蘸,而不是通過(guò)對(duì)象本身負(fù)責(zé)自己的創(chuàng)建和解決自己的依賴裁眯。依賴注入的主要目的是解耦,體現(xiàn)一種“組合”的理念闺魏,如果你希望你的類具備某項(xiàng)功能的時(shí)候,是繼承自具有此項(xiàng)功能的父類好俯画,還是組合另一個(gè)具有這個(gè)功能的類好析桥,答案不言而喻。

Spring IoC容器負(fù)責(zé)創(chuàng)建Bean艰垂,并將功能類Bean注入到你需要的Bean中泡仗。Spring提供xml配置、注解猜憎、Java配置娩怎、groovy配置實(shí)現(xiàn)Bean的創(chuàng)建和注入。

聲明Bean的注解:

@Component組件:沒有明確的角色
@Service:業(yè)務(wù)邏輯層使用
@Repository:數(shù)據(jù)訪問(wèn)層(DAO)使用
@Controller:展現(xiàn)層使用

注入Bean的注解

@Autowire:Spring提供的注解
@Inject:JSR-330標(biāo)準(zhǔn)提供的注解
@Resource:JSR-250標(biāo)準(zhǔn)提供的注解
一般情況下通用

1.3.Java配置

Spring4.x推薦的配置方式胰柑,也是Spring Boot推薦的配置方式截亦,可以完全替代xml配置。
@Configuration:Java配置配置方式柬讨,聲明當(dāng)前類是一個(gè)配置類崩瓤,與使用xml配置文件效果一樣
@ComponentScan 自動(dòng)掃描報(bào)名下所有使用@Service @Component @Repository @Controller的類并注冊(cè)為Bean
@Bean注解在方法上,聲明當(dāng)前方法的返回值為一個(gè)Bean

2.MVC模型

MVC模型是一種架構(gòu)型的模式踩官,本身不引入新功能却桶,只是幫助我們將開發(fā)的結(jié)構(gòu)組織的更加合理,使展示與模型分離、流程控制邏輯颖系、業(yè)務(wù)邏輯調(diào)用與展示邏輯分離嗅剖。


1.jpg

Web端的開發(fā)發(fā)展流程如下圖

2.jpg

這里只講述服務(wù)到工作者:Front Controller + Application Controller + Page Controller + Context
運(yùn)行流程如下:

3.jpg

我們回顧了整個(gè)web開發(fā)架構(gòu)的發(fā)展歷程,可能不同的web層框架在細(xì)節(jié)處理方面不同嘁扼,但的目的是一樣的:

1信粮、 干凈的web表現(xiàn)層:

  • 模型和視圖的分離;
  • 控制器中的控制邏輯與功能處理分離(收集并封裝參數(shù)到模型對(duì)象偷拔、業(yè)務(wù)對(duì)象調(diào)用)蒋院;
  • 控制器中的視圖選擇與具體視圖技術(shù)分離。

2莲绰、 輕薄的web表現(xiàn)層:

  • 做的事情越少越好欺旧,薄薄的,不應(yīng)該包含無(wú)關(guān)代碼蛤签;
  • 只負(fù)責(zé)收集并組織參數(shù)到模型對(duì)象辞友,啟動(dòng)業(yè)務(wù)對(duì)象的調(diào)用;
  • 控制器只返回邏輯視圖名并由相應(yīng)的應(yīng)用控制器來(lái)選擇具體使用的視圖策略震肮;
  • 盡量少使用框架特定API称龙,保證容易測(cè)試。

3.Spring MVC模型

Spring Web MVC是一種基于Java的實(shí)現(xiàn)了Web MVC設(shè)計(jì)模式的請(qǐng)求驅(qū)動(dòng)類型的輕量級(jí)Web框架戳晌,即使用了MVC架構(gòu)模式的思想鲫尊,將web層進(jìn)行職責(zé)解耦,基于請(qǐng)求驅(qū)動(dòng)指的就是使用請(qǐng)求-響應(yīng)模型沦偎,框架的目的就是幫助我們簡(jiǎn)化開發(fā)

Spring Web MVC也是服務(wù)到工作者模式的實(shí)現(xiàn)疫向,但進(jìn)行可優(yōu)化。

  • 前端控制器是DispatcherServlet豪嚎;
  • 應(yīng)用控制器其實(shí)拆為處理器映射器(Handler Mapping)進(jìn)行處理器管理和視圖解析器****(View Resolver)進(jìn)行視圖管理搔驼;
  • 頁(yè)面控制器/動(dòng)作/處理器為Controller****接口(僅包含ModelAndView handleRequest(request, response) 方法)的實(shí)現(xiàn)(也可以是任何的POJO類);
  • 支持本地化(Locale)解析侈询、主題(Theme)解析及文件上傳等舌涨;
  • 提供了非常靈活的數(shù)據(jù)驗(yàn)證、格式化和數(shù)據(jù)綁定機(jī)制扔字;
  • 提供了強(qiáng)大的約定大于配置(慣例優(yōu)先原則)的契約式編程支持囊嘉。

3.1.Spring Web MVC能做什么

  • 讓我們能非常簡(jiǎn)單的設(shè)計(jì)出干凈的Web層和薄薄的Web層;
  • 進(jìn)行更簡(jiǎn)潔的Web層的開發(fā)革为;
  • 天生與Spring框架集成(如IoC容器哗伯、AOP等);
  • 提供強(qiáng)大的約定大于配置的契約式編程支持篷角;
  • 能簡(jiǎn)單的進(jìn)行Web層的單元測(cè)試焊刹;
  • 支持靈活的URL到頁(yè)面控制器的映射;
  • 非常容易與其他視圖技術(shù)集成,如Velocity虐块、FreeMarker等等俩滥,因?yàn)槟P蛿?shù)據(jù)不放在特定的API里,而是放在一個(gè)Model里(Map數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)贺奠,因此很容易被其他框架使用)霜旧;
  • 非常靈活的數(shù)據(jù)驗(yàn)證、格式化和數(shù)據(jù)綁定機(jī)制儡率,能使用任何對(duì)象進(jìn)行數(shù)據(jù)綁定挂据,不必實(shí)現(xiàn)特定框架的API;
  • 提供一套強(qiáng)大的JSP標(biāo)簽庫(kù)儿普,簡(jiǎn)化JSP開發(fā)崎逃;
  • 支持靈活的本地化、主題等解析眉孩;
  • 更加簡(jiǎn)單的異常處理个绍;
  • 對(duì)靜態(tài)資源的支持;
  • 支持Restful風(fēng)格浪汪。

3.2.Spring Web MVC處理請(qǐng)求的流程

4.jpg

具體執(zhí)行步驟如下:
1巴柿、 首先用戶發(fā)送請(qǐng)求—>前端控制器,前端控制器根據(jù)請(qǐng)求信息(如URL)來(lái)決定選擇哪一個(gè)頁(yè)面控制器進(jìn)行處理并把請(qǐng)求委托給它死遭,即以前的控制器的控制邏輯部分广恢;圖中的1、2步驟呀潭;

2钉迷、 頁(yè)面控制器接收到請(qǐng)求后,進(jìn)行功能處理蜗侈,首先需要收集和綁定請(qǐng)求參數(shù)到一個(gè)對(duì)象篷牌,這個(gè)對(duì)象在Spring Web MVC中叫命令對(duì)象睡蟋,并進(jìn)行驗(yàn)證踏幻,然后將命令對(duì)象委托給業(yè)務(wù)對(duì)象進(jìn)行處理;處理完畢后返回一個(gè)ModelAndView(模型數(shù)據(jù)和邏輯視圖名)戳杀;圖2-1中的3该面、4、5步驟信卡;

3隔缀、 前端控制器收回控制權(quán),然后根據(jù)返回的邏輯視圖名傍菇,選擇相應(yīng)的視圖進(jìn)行渲染猾瘸,并把模型數(shù)據(jù)傳入以便視圖渲染;圖2-1中的步驟6、7牵触;

4淮悼、 前端控制器再次收回控制權(quán),將響應(yīng)返回給用戶揽思,圖2-1中的步驟8袜腥;至此整個(gè)結(jié)束

3.3.疑惑(后面解答)

1、 請(qǐng)求如何給前端控制器钉汗?
2羹令、 前端控制器如何根據(jù)請(qǐng)求信息選擇頁(yè)面控制器進(jìn)行功能處理?
3损痰、 如何支持多種頁(yè)面控制器呢福侈?
4、 如何頁(yè)面控制器如何使用業(yè)務(wù)對(duì)象徐钠?
5癌刽、 頁(yè)面控制器如何返回模型數(shù)據(jù)?
6尝丐、 前端控制器如何根據(jù)頁(yè)面控制器返回的邏輯視圖名選擇具體的視圖進(jìn)行渲染显拜?
7、 不同的視圖技術(shù)如何使用相應(yīng)的模型數(shù)據(jù)?

4.Spring核心框架

5.jpg

核心架構(gòu)的具體流程步驟如下:
1瓷胧、 用戶發(fā)送請(qǐng)求 -> DispatcherServlet亚铁,前端控制器收到請(qǐng)求后自己不進(jìn)行處理,而是委托給其他的解析器進(jìn)行處理譬淳,作為統(tǒng)一訪問(wèn)點(diǎn),進(jìn)行全局的流程控制盹兢;
2邻梆、 DispatcherServle -> HandlerMapping:HandlerMapping將會(huì)把請(qǐng)求映射為HandlerExecutionChain對(duì)象(包含一個(gè)Handler處理器(頁(yè)面控制器)對(duì)象、多個(gè)HandlerInterceptor攔截器)對(duì)象绎秒,通過(guò)這種策略模式浦妄,很容易添加新的映射策略;
3见芹、 DispatcherServlet -> HandlerAdapter:HandlerAdapter將會(huì)把處理器包裝為適配器剂娄,從而支持多種類型的處理器,即適配器設(shè)計(jì)模式的應(yīng)用玄呛,從而很容易支持很多類型的處理器阅懦;
4、 HandlerAdapter -> 處理器:HandlerAdapter將會(huì)根據(jù)適配的結(jié)果調(diào)用真正的處理器的功能處理方法徘铝,完成功能處理耳胎;并返回一個(gè)ModelAndView對(duì)象(包含模型數(shù)據(jù)惯吕、邏輯視圖名);
5怕午、 ModelAndView -> ViewResolver:ViewResolver將把邏輯視圖名解析為具體的View混埠,通過(guò)這種策略模式,很容易更換其他視圖技術(shù)诗轻;
6扳炬、 渲染View :View會(huì)根據(jù)傳進(jìn)來(lái)的Model模型數(shù)據(jù)進(jìn)行渲染劝术,此處的Model實(shí)際是一個(gè)Map數(shù)據(jù)結(jié)構(gòu),因此很容易支持其他視圖技術(shù);
7、 DispatcherServlet****響應(yīng):返回控制權(quán)給DispatcherServlet月腋,由DispatcherServlet返回響應(yīng)給用戶寨躁,到此一個(gè)流程結(jié)束放钦。

此處我們只是講了核心流程颓屑,沒有考慮攔截器器腋、本地解析措左、文件上傳解析等钳枕。
現(xiàn)在可以回答上述提出的問(wèn)題:

1昔瞧、 請(qǐng)求如何給前端控制器酬荞?
答:這個(gè)應(yīng)該在web.xml中進(jìn)行部署描述
2陨亡、 前端控制器如何根據(jù)請(qǐng)求信息選擇頁(yè)面控制器進(jìn)行功能處理虐急?
答:我們需要配置HandlerMapping進(jìn)行映射
3、 如何支持多種頁(yè)面控制器呢滔迈?
答:配置HandlerAdapter從而支持多種類型的頁(yè)面控制器
4止吁、 頁(yè)面控制器如何使用業(yè)務(wù)對(duì)象?
答:利用Spring IoC容器的依賴注入功能
5燎悍、 頁(yè)面控制器如何返回模型數(shù)據(jù)敬惦?
答:使用ModelAndView返回
6、 前端控制器如何根據(jù)頁(yè)面控制器返回的邏輯視圖名選擇具體的視圖進(jìn)行渲染谈山?
答: 使用ViewResolver進(jìn)行解析
7俄删、 不同的視圖技術(shù)如何使用相應(yīng)的模型數(shù)據(jù)?
答:因?yàn)镸odel是一個(gè)Map數(shù)據(jù)結(jié)構(gòu)奏路,很容易支持其他視圖技術(shù)

org.springframework.web.servlet.DispatcherServlet # 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 {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.(通過(guò)HandlerMapping映射獲取)
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                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()) {
                        logger.debug(
"Last-Modified value for [" + getRequestUri(request) + "] is: " 
+ lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) 
&& isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler. 適配器執(zhí)行處理器
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                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);
            }
            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 {
            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.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

通過(guò)閱讀源碼畴椰,可以看出具體的核心開發(fā)步驟:
1、 DispatcherServlet在web.xml中的部署描述鸽粉,從而攔截請(qǐng)求到Spring Web MVC
2斜脂、 HandlerMapping的配置,從而將請(qǐng)求映射到處理器
3触机、 HandlerAdapter的配置帚戳,從而支持多種類型的處理器
4、 ViewResolver的配置儡首,從而將邏輯視圖名解析為具體視圖技術(shù)
5片任、 處理器(頁(yè)面控制器)的配置,從而進(jìn)行功能處理

5.注解式開發(fā)

Spring2.5引入注解式處理器支持蔬胯,通過(guò)@Controller 和 @RequestMapping注解定義我們的處理器類对供。并且提供了一組強(qiáng)大的注解:

需要通過(guò)處理器映射DefaultAnnotationHandlerMapping和處理器適配器AnnotationMethodHandlerAdapter來(lái)開啟支持@Controller 和 @RequestMapping注解的處理器。

  • @Controller:用于標(biāo)識(shí)是處理器類笔宿;
  • @RequestMapping:請(qǐng)求到處理器功能方法的映射規(guī)則犁钟;
  • @RequestParam:請(qǐng)求參數(shù)到處理器功能處理方法的方法參數(shù)上的綁定;
  • @ModelAttribute:請(qǐng)求參數(shù)到命令對(duì)象的綁定泼橘;
  • @SessionAttributes:用于聲明session級(jí)別存儲(chǔ)的屬性涝动,放置在處理器類上,通常列出模型屬性(如@ModelAttribute)對(duì)應(yīng)的名稱炬灭,則這些屬性會(huì)透明的保存到session中醋粟;
  • @InitBinder:自定義數(shù)據(jù)綁定注冊(cè)支持,用于將請(qǐng)求參數(shù)轉(zhuǎn)換到命令對(duì)象屬性的對(duì)應(yīng)類型重归;

Spring3.0引入RESTful架構(gòu)風(fēng)格支持(通過(guò)@PathVariable注解和一些其他特性支持),且又引入了更多的注解支持:

  • @CookieValue:cookie數(shù)據(jù)到處理器功能處理方法的方法參數(shù)上的綁定米愿;
  • @RequestHeader:請(qǐng)求頭(header)數(shù)據(jù)到處理器功能處理方法的方法參數(shù)上的綁定;
  • @RequestBody:請(qǐng)求的body體的綁定(通過(guò)HttpMessageConverter進(jìn)行類型轉(zhuǎn)換)鼻吮;
  • @ResponseBody:處理器功能處理方法的返回值作為響應(yīng)體(通過(guò)HttpMessageConverter進(jìn)行類型轉(zhuǎn)換)育苟;
  • @ResponseStatus:定義處理器功能處理方法/異常處理器返回的狀態(tài)碼和原因;
  • @ExceptionHandler:注解式聲明異常處理器椎木;
  • @PathVariable:請(qǐng)求URI中的模板變量部分到處理器功能處理方法的方法參數(shù)上的綁定违柏,從而支持RESTful架構(gòu)風(fēng)格的URI;

6.DispatcherServlet

DispatcherServlet主要用作職責(zé)調(diào)度工作香椎,本身主要用于控制流程漱竖,主要職責(zé)如下:

1、 文件上傳解析畜伐,如果請(qǐng)求類型是multipart將通過(guò)MultipartResolver進(jìn)行文件上傳解析馍惹;
2、 通過(guò)HandlerMapping玛界,將請(qǐng)求映射到處理器(返回一個(gè)HandlerExecutionChain万矾,它包括一個(gè)處理器、多個(gè)HandlerInterceptor攔截器)慎框;
3勤众、 通過(guò)HandlerAdapter支持多種類型的處理器(HandlerExecutionChain中的處理器);
4鲤脏、 通過(guò)ViewResolver解析邏輯視圖名到具體視圖實(shí)現(xiàn)们颜;
5、 本地化解析猎醇;
6窥突、 渲染具體的視圖等;
7硫嘶、 如果執(zhí)行過(guò)程中遇到異常將交給HandlerExceptionResolver來(lái)解析阻问。

DispatcherServlet主要負(fù)責(zé)流程的控制,而且在流程中的每個(gè)關(guān)鍵點(diǎn)都是很容易擴(kuò)展的沦疾。

DispatcherServlet默認(rèn)使用WebApplicationContext作為上下文称近,因此我們來(lái)看一下該上下文中有哪些特殊的Bean:

1第队、 Controller:處理器/頁(yè)面控制器,做的是MVC中的C的事情刨秆,但控制邏輯轉(zhuǎn)移到前端控制器了凳谦,用于對(duì)請(qǐng)求進(jìn)行處理;

2衡未、 HandlerMapping:請(qǐng)求到處理器的映射尸执,如果映射成功返回一個(gè)HandlerExecutionChain對(duì)象(包含一個(gè)Handler處理器(頁(yè)面控制器)對(duì)象、多個(gè)HandlerInterceptor攔截器)對(duì)象缓醋;如BeanNameUrlHandlerMapping將URL與Bean名字映射如失,映射成功的Bean就是此處的處理器;

3送粱、 HandlerAdapter:HandlerAdapter將會(huì)把處理器包裝為適配器褪贵,從而支持多種類型的處理器,即適配器設(shè)計(jì)模式的應(yīng)用抗俄,從而很容易支持很多類型的處理器竭鞍;如SimpleControllerHandlerAdapter將對(duì)實(shí)現(xiàn)了Controller接口的Bean進(jìn)行適配,并且掉處理器的handleRequest方法進(jìn)行功能處理橄镜;

4偎快、 ViewResolver:ViewResolver將把邏輯視圖名解析為具體的View,通過(guò)這種策略模式洽胶,很容易更換其他視圖技術(shù)晒夹;如InternalResourceViewResolver將邏輯視圖名映射為jsp視圖;

5姊氓、 LocalResover:本地化解析丐怯,因?yàn)镾pring支持國(guó)際化,因此LocalResover解析客戶端的Locale信息從而方便進(jìn)行國(guó)際化翔横;

6读跷、 ThemeResovler:主題解析,通過(guò)它來(lái)實(shí)現(xiàn)一個(gè)頁(yè)面多套風(fēng)格禾唁,即常見的類似于軟件皮膚效果效览;

7、 MultipartResolver:文件上傳解析荡短,用于支持文件上傳丐枉;

8、 HandlerExceptionResolver:處理器異常解析掘托,可以將異常映射到相應(yīng)的統(tǒng)一錯(cuò)誤界面瘦锹,從而顯示用戶友好的界面(而不是給用戶看到具體的錯(cuò)誤信息);

9、 RequestToViewNameTranslator:當(dāng)處理器沒有返回邏輯視圖名等相關(guān)信息時(shí)弯院,自動(dòng)將請(qǐng)求URL映射為邏輯視圖名辱士;

10、 FlashMapManager:用于管理FlashMap的策略接口听绳,F(xiàn)lashMap用于存儲(chǔ)一個(gè)請(qǐng)求的輸出颂碘,當(dāng)進(jìn)入另一個(gè)請(qǐng)求時(shí)作為該請(qǐng)求的輸入,通常用于重定向場(chǎng)景辫红。

ContextLoaderListener初始化的上下文加載的Bean是對(duì)于整個(gè)應(yīng)用程序共享的凭涂,不管是使用什么表現(xiàn)層技術(shù)祝辣,一般如DAO層贴妻、Service層Bean;

DispatcherServlet初始化的上下文加載的Bean是只對(duì)Spring Web MVC有效的Bean蝙斜,如Controller名惩、HandlerMapping、HandlerAdapter等等孕荠,該初始化上下文應(yīng)該只加載Web相關(guān)組件娩鹉。

Controller

Controller控制器,是MVC中的部分C稚伍,為什么是部分呢弯予?因?yàn)榇颂幍目刂破髦饕?fù)責(zé)功能處理部分:

1、 收集个曙、驗(yàn)證請(qǐng)求參數(shù)并綁定到命令對(duì)象锈嫩;
2、 將命令對(duì)象交給業(yè)務(wù)對(duì)象垦搬,由業(yè)務(wù)對(duì)象處理并返回模型數(shù)據(jù)呼寸;
3、 返回ModelAndView(Model部分是業(yè)務(wù)對(duì)象返回的模型數(shù)據(jù)猴贰,視圖部分為邏輯視圖名)对雪。

還記得DispatcherServlet嗎?主要負(fù)責(zé)整體的控制流程的調(diào)度部分:
1米绕、負(fù)責(zé)將請(qǐng)求委托給控制器進(jìn)行處理瑟捣;
2、根據(jù)控制器返回的邏輯視圖名選擇具體的視圖進(jìn)行渲染(并把模型數(shù)據(jù)傳入)栅干。
因此MVC中完整的C(包含控制邏輯+功能處理)由(DispatcherServlet + Controller)組成蝶柿。

Controller繼承關(guān)系圖

6.jpg

7.攔截器

請(qǐng)求的映射分為如下幾種:

  • URL路徑映射:使用URL映射請(qǐng)求到處理器的功能處理方法;
  • 請(qǐng)求方法映射限定:如限定功能處理方法只處理GET請(qǐng)求非驮;
  • 請(qǐng)求參數(shù)映射限定:如限定只處理包含“abc”請(qǐng)求參數(shù)的請(qǐng)求交汤;
  • 請(qǐng)求頭映射限定:如限定只處理“Accept=application/json”的請(qǐng)求。

可以通過(guò)在一個(gè)POJO類上放置@Controller或@RequestMapping,即可把一個(gè)POJO類變身為處理器芙扎;
@RequestMapping(value = "/hello") 請(qǐng)求URL(/hello) 到 處理器的功能處理方法的映射星岗;

模型數(shù)據(jù)和邏輯視圖名的返回。

package cn.javass.chapter6.web.controller;
//省略import
@Controller // 或 @RequestMapping ①將一個(gè)POJO類聲明為處理器
public class HelloWorldController {
    @RequestMapping(value = "/hello") //②請(qǐng)求URL到處理器功能處理方法的映射
    public ModelAndView helloWorld() {
        //1戒洼、收集參數(shù)
        //2俏橘、綁定參數(shù)到命令對(duì)象
        //3、調(diào)用業(yè)務(wù)對(duì)象
        //4圈浇、選擇下一個(gè)頁(yè)面
        ModelAndView mv = new ModelAndView();
        //添加模型數(shù)據(jù) 可以是任意的POJO對(duì)象
        mv.addObject("message", "Hello World!");
        //設(shè)置邏輯視圖名寥掐,視圖解析器會(huì)根據(jù)該名字解析到具體的視圖頁(yè)面
        mv.setViewName("hello");
        return mv;  //③ 模型數(shù)據(jù)和邏輯視圖名
    }
}

如果您使用的是Spring3.1之前版本,開啟注解式處理器支持的配置為:

<!—Spring3.1之前的注解 HandlerMapping -->
<bean 
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

<!—Spring3.1之前的注解 HandlerAdapter -->
<bean 
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

如果您使用的Spring3.1開始的版本磷蜀,建議使用RequestMappingHandlerMapping和RequestMappingHandlerAdapter召耘。

<!--Spring3.1開始的注解 HandlerMapping -->
<bean 
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--Spring3.1開始的注解 HandlerAdapter -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

運(yùn)行流程:

7.jpg
@Controller  
public class HelloWorldController {  
……  
}  

推薦使用這種方式聲明處理器,它和我們的@Service褐隆、@Repository很好的對(duì)應(yīng)了我們常見的三層開發(fā)架構(gòu)的組件污它。

@RequestMapping

@RequestMapping  
public class HelloWorldController {  

}  

這種方式也是可以工作的,但如果在類上使用@ RequestMapping注解一般是用于

窄化功能處理方法的映射的庶弃,詳見6.4.3衫贬。

package cn.javass.chapter6.web.controller;  
@Controller  
@RequestMapping(value="/user")                 //①處理器的通用映射前綴  
public class HelloWorldController2 {  
    @RequestMapping(value = "/hello2")        //②相對(duì)于①處的映射進(jìn)行窄化  
    public ModelAndView helloWorld() {  

    }  
}  

窄化請(qǐng)求映射

package cn.javass.chapter6.web.controller;  
@Controller  
@RequestMapping(value="/user")                 //①處理器的通用映射前綴  
public class HelloWorldController2 {  
    @RequestMapping(value = "/hello2")        //②相對(duì)于①處的映射進(jìn)行窄化  
    public ModelAndView helloWorld() {  
         //省略實(shí)現(xiàn)  
    }  
}  

①類上的@RequestMapping(value="/user") 表示處理器的通用請(qǐng)求前綴;
②處理器功能處理方法上的是對(duì)①處映射的窄化歇攻。

因此http://localhost:9080/springmvc-chapter6/hello2 無(wú)法映射到HelloWorldController2的 helloWorld功能處理方法固惯;而http://localhost:9080/springmvc-chapter6/user/hello2是可以的。

8.jpg

窄化請(qǐng)求映射可以認(rèn)為是方法級(jí)別的@RequestMapping繼承類級(jí)別的@RequestMapping缴守,窄化請(qǐng)求映射還有其他方式葬毫,如在類級(jí)別指定URL,而方法級(jí)別指定請(qǐng)求方法類型或參數(shù)等等


9.jpg

Servlet

Servlet是一套Web應(yīng)用的開發(fā)規(guī)范斧散,我們按照這套規(guī)范編碼就可以實(shí)現(xiàn)一個(gè)Web應(yīng)用供常,使其在Web容器中運(yùn)行。我們最開始學(xué)習(xí)J2EE時(shí)鸡捐,學(xué)習(xí)和創(chuàng)建的就是Servlet的實(shí)現(xiàn)類栈暇,后來(lái)學(xué)習(xí)了MVC框架以后,尤其是SpringMVC箍镜,就很少直接創(chuàng)建Servlet的實(shí)現(xiàn)類了源祈。雖然SpringMVC簡(jiǎn)化和隱藏了Servlet,但是我們也要了解Servlet的運(yùn)行原理色迂,這樣對(duì)了解SpringMVC的原理也很有幫助香缺。

Servlet 容器

Tomcat 的容器等級(jí)中,Context 容器是直接管理 Servlet 在容器中的包裝類 Wrapper歇僧,所以 Context 容器如何運(yùn)行將直接影響 Servlet 的工作方式图张。


10.jpg

真正管理 Servlet 的容器是 Context 容器锋拖,一個(gè) Context 對(duì)應(yīng)一個(gè) Web 工程,在 Tomcat 的配置文件中可以很容易發(fā)現(xiàn)這一點(diǎn)祸轮,如下:

<Context path="/projectOne " docBase="D:\projects\projectOne" 
 reloadable="true" />

當(dāng) Context容器初始化狀態(tài)設(shè)為init 時(shí)兽埃,添加在 Contex 容器的 Listener 將會(huì)被調(diào)用。ContextConfig 繼承了 LifecycleListener 接口适袜,它是在調(diào)用Tomcat的addWebapp方法時(shí)被加入到 StandardContext 容器中柄错。ContextConfig 類會(huì)負(fù)責(zé)整個(gè) Web 應(yīng)用的配置文件的解析工作。

ContextConfig 的 init 方法將會(huì)主要完成以下工作:

  • 創(chuàng)建用于解析 xml 配置文件的 contextDigester 對(duì)象
  • 讀取默認(rèn) context.xml 配置文件苦酱,如果存在解析它
  • 讀取默認(rèn) Host 配置文件售貌,如果存在解析它
  • 讀取默認(rèn) Context 自身的配置文件,如果存在解析它
  • 設(shè)置 Context 的 DocBase

ContextConfig 的 init 方法完成后疫萤,Context 容器的會(huì)執(zhí)行 startInternal 方法颂跨,這個(gè)方法啟動(dòng)邏輯比較復(fù)雜,主要包括如下幾個(gè)部分:

  • 創(chuàng)建讀取資源文件的對(duì)象
  • 創(chuàng)建 ClassLoader 對(duì)象
  • 設(shè)置應(yīng)用的工作目錄
  • 啟動(dòng)相關(guān)的輔助類如:logger给僵、realm毫捣、resources 等
  • 修改啟動(dòng)狀態(tài)详拙,通知感興趣的觀察者(Web 應(yīng)用的配置)
  • 子容器的初始化
  • 獲取 ServletContext 并設(shè)置必要的參數(shù)
  • 初始化“l(fā)oad on startup”的 Servlet

為什么要將 Servlet 包裝成 StandardWrapper 而不直接是 Servlet 對(duì)象帝际。這里 StandardWrapper 是 Tomcat 容器中的一部分,它具有容器的特征饶辙,而 Servlet 為了一個(gè)獨(dú)立的 web 開發(fā)標(biāo)準(zhǔn)蹲诀,不應(yīng)該強(qiáng)耦合在 Tomcat 中。除了將 Servlet 包裝成 StandardWrapper 并作為子容器添加到 Context 中弃揽,其它的所有 web.xml 屬性都被解析到 Context 中脯爪,所以說(shuō) Context 容器才是真正運(yùn)行 Servlet 的 Servlet 容器。一個(gè) Web 應(yīng)用對(duì)應(yīng)一個(gè) Context 容器矿微,容器的配置屬性由 應(yīng)用的 web.xml 指定痕慢,這樣我們就能理解 web.xml 到底起到什么作用了。

那服務(wù)器是如何根據(jù)這個(gè) URL 來(lái)達(dá)到正確的 Servlet 容器中的呢涌矢?

Tomcat7.0 中這件事很容易解決掖举,因?yàn)檫@種映射工作有專門一個(gè)類來(lái)完成的,這個(gè)就是 org.apache.tomcat.util.http.mapper娜庇,這個(gè)類保存了 Tomcat 的 Container 容器中的所有子容器的信息塔次,當(dāng) org.apache.catalina.connector. Request 類在進(jìn)入 Container 容器之前,mapper 將會(huì)根據(jù)這次請(qǐng)求的 hostnane 和 contextpath 將 host 和 context 容器設(shè)置到 Request 的 mappingData 屬性中名秀。所以當(dāng) Request 進(jìn)入 Container 容器之前励负,它要訪問(wèn)那個(gè)子容器這時(shí)就已經(jīng)確定了。

Session 工作的時(shí)序圖

11.jpg

Servlet 中的 Listener


12.png

用@Configuration注解該類匕得,等價(jià) 與XML中配置beans继榆;用@Bean標(biāo)注方法等價(jià)于XML中配置bean。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市略吨,隨后出現(xiàn)的幾起案子攒发,更是在濱河造成了極大的恐慌,老刑警劉巖晋南,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惠猿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡负间,警方通過(guò)查閱死者的電腦和手機(jī)偶妖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)政溃,“玉大人趾访,你說(shuō)我怎么就攤上這事《” “怎么了扼鞋?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)愤诱。 經(jīng)常有香客問(wèn)我云头,道長(zhǎng),這世上最難降的妖魔是什么淫半? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任溃槐,我火速辦了婚禮,結(jié)果婚禮上科吭,老公的妹妹穿的比我還像新娘昏滴。我一直安慰自己,他們只是感情好对人,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布谣殊。 她就那樣靜靜地躺著,像睡著了一般牺弄。 火紅的嫁衣襯著肌膚如雪姻几。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天猖闪,我揣著相機(jī)與錄音鲜棠,去河邊找鬼。 笑死培慌,一個(gè)胖子當(dāng)著我的面吹牛豁陆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吵护,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼盒音,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼表鳍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起祥诽,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤譬圣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后雄坪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厘熟,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年维哈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绳姨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阔挠,死狀恐怖飘庄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情购撼,我是刑警寧澤跪削,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站迂求,受9級(jí)特大地震影響碾盐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锁摔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一廓旬、第九天 我趴在偏房一處隱蔽的房頂上張望哼审。 院中可真熱鬧谐腰,春花似錦、人聲如沸涩盾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)春霍。三九已至砸西,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間址儒,已是汗流浹背芹枷。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莲趣,地道東北人鸳慈。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像喧伞,于是被迫代替她去往敵國(guó)和親走芋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绩郎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容