spring mvc工作機(jī)制

1. 配置

springMVC的核心是DispatcherServlet,它實(shí)現(xiàn)了HttpServlet類,在web.xml中引入配置:

 <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:springMVC.xml</param-value>
  </context-param>

  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

Servlet容器tomcat在初始化DispatcherServlet時(shí)蔓姚,會(huì)創(chuàng)建一個(gè)新的applicationContext(默認(rèn)是XmlWebApplicationContext)去加載參數(shù) contextConfigLocation指定的spring xml配置文件收苏。

在基于注解的springMVC配置中熬甚,需要在spring配置文件中引入如下配置:

   <context:component-scan base-package="..."/>
   <mvc:annotation-driven/>

注解<mvc:annotation-driven/>默認(rèn)會(huì)在spring上下文中引入以下這些bean:

  • RequestMappingHandlerMapping

  • RequestMappingHandlerAdapter
    兩者配合處理基于注解的(@Controller@RequestMapping等等)spring mvc使用

    RequestMappingHandlerMapping會(huì)讀取spring上下文中所有使用 @Controller或者@RequestMapping注解的bean并解析他的方法作為handler

  • BeanNameUrlHandlerMapping
    處理Struct風(fēng)格的url孙蒙,即將url作為bean name獲取到bean,這種時(shí)候一個(gè)url請求對應(yīng)一個(gè)bean(即你的controller類)悲雳,這個(gè)時(shí)候你的controller類可以實(shí)現(xiàn)AbstractController抽象類

    BeanNameUrlHandlerMapping會(huì)讀取spring上下文中所有名字以'/'開始的bean作為handler

  • SimpleControllerHandlerAdapter
    配合BeanNameUrlHandlerMapping一起使用挎峦。

2. SpringMVC工作流程

如下圖:

springMVC流程.png
  1. DispatcherServlet作為前端控制器,請求會(huì)先經(jīng)過它合瓢,進(jìn)入到其doDispatch方法中坦胶,DispatcherServlet可以有多個(gè)HandlerMapping和HandlerAdapter,DispatcherServlet根據(jù)按序排列它們。

  2. DispatcherServlet會(huì)遍歷所有HandlerMapping顿苇,并但返回第一個(gè)能夠處理當(dāng)前request的HandlerMapping對像峭咒,并調(diào)用其getHandler方法返回HandlerExecutionChain

  3. HandlerExecutionChain由一個(gè)handler和若干HandlerInterceptor組成,其中handler其實(shí)就是用戶的業(yè)務(wù)邏輯實(shí)現(xiàn)岖圈,由創(chuàng)建這個(gè)HandlerExecutionChain的HandlerMapping設(shè)置讹语,比如RequestMappingHandlerMapping的handler其實(shí)就是Contorller的某個(gè)映射當(dāng)前request url的method的封裝。

  4. DispatcherServlet調(diào)用所有HandlerInterceptor的preHandle處理request

  5. HandlerAdaptor是HandlerExecutionChain的handler的適配器蜂科,對于有多個(gè)HandlerAdaptor的情況顽决,返回第一個(gè)能夠適配handler的適配器。

    不同的適配器完成的功能不一樣导匣,一般都是在激活handler進(jìn)入用戶業(yè)務(wù)邏輯前做一些預(yù)處理才菠,然后進(jìn)入用戶業(yè)務(wù)邏輯,最終返回ModelAnView對象贡定。RequestMappingHandlerAdapter這種比較復(fù)雜的會(huì)處理@PathVariable,@ModelAttribute這個(gè)注解完成參數(shù)handler的參數(shù)的設(shè)置赋访,然后調(diào)用handler的方法進(jìn)入用戶業(yè)務(wù)邏輯,

  6. 接下來調(diào)用HandlerExecutionChain中所有interceptor的postHandle

  7. 將ModelAndView交給ViewResolver處理

3. HandlerMapping

接口HandlerMapping只有一個(gè)接口方法:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

getHandler里一般是判斷能不能處理這個(gè)request缓待,不能返回null蚓耽,否則返回HandlerExecutionChain,類成員如下:

class HandlerExecutionChain{
    // handler一般是用戶業(yè)務(wù)邏輯的封裝
    private final Object handler;
    // 攔截器
    private HandlerInterceptor[] interceptors;
     ...
    // 上面第2節(jié)中步驟 4旋炒,它依次調(diào)用interceptors中每一個(gè)interceptor的preHandle
    // 只要有一個(gè)interceptor的preHandle返回false就不再走下去步悠,直接返回false,本次對request處理結(jié)束瘫镇,不再繼續(xù)第2節(jié)中其他后續(xù)流程
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) 
    // 上面第2節(jié)中步驟 6
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
}

-------攔截器HandlerInterceptor----------
public interface HandlerInterceptor {
    // 返回false就不會(huì)調(diào)用后續(xù)攔截器鼎兽,意味著本次request處理結(jié)束
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;
    // preHandler返回false,或者出現(xiàn)異常铣除,或者正常返回時(shí)調(diào)用
    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

3.1 HandlerMapping的一些實(shí)現(xiàn)類

1. RequestMappingHandlerMapping
它是用來處理使用了@Controller或者@RequestMapping注解的用戶controller層面的業(yè)務(wù)邏輯類的谚咬。
像下面這樣:

@Controller
@RequestMapping("/api/echo")
public class EchoTime {
    @RequestMapping(value = "/currentDate", method = RequestMethod.GET, produces = "application/json")
    public Date currentDate(){
        Date d = new Date();
        System.out.println("in currentDate");
        return d;
    }
}

RequestMappingHandlerMapping獲取當(dāng)前applicationContext中所有使用了@Controller或者@RequestMapping注解的bean,然后解析這些bean中使用了@RequestMapping注解的方法尚粘,將這些方法封裝成HandlerMethod,然后建立url到這個(gè)HandlerMethod的映射择卦。

  • RequestMappingHandlerMapping # getHandler返回的就是包裝了HandlerMethod對象HandlerExecutionChain
  • RequestMappingHandlerMapping返回的HandlerExecutionChain中interceptor處理用戶在spring的配置文件中顯示指定以外,應(yīng)該默認(rèn)會(huì)包含當(dāng)前上下文中所有實(shí)現(xiàn)了MappedInterceptor的bean

2. BeanNameUrlHandlerMapping
它將url映射到bean郎嫁。和在RequestMappingHandlerMapping中互捌,一個(gè)url映射到一個(gè)contorller的方法不同。
同時(shí)BeanNameUrlHandlerMapping要求bean name必須是以'/'開始的行剂。BeanNameUrlHandlerMapping默認(rèn)會(huì)加載當(dāng)前applicationContext中所有bean name以‘/’開始bean秕噪,然后以bean name作為url建立到bean的映射。 這種方式中一般要求用戶的controller類實(shí)現(xiàn)Controller接口厚宰,或者AbstractController抽象類腌巾。如下所示:

// bean name要以‘/’開始遂填,
@Controller("/test.do")
public class EchoController extends AbstractController {
    protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("date", "date");
        return modelAndView;
    }
}

BeanNameUrlHandlerMapping返回的HandlerExecutionChain的handler就是匹配url的bean的實(shí)例。

3. SimpleUrlHandlerMapping
這是一種很靈活的Handler Mapping澈蝙,能夠直接指定url到bean的映射, 可以在配置文件中指定:

<bean id="echoContorller" class="me.eric.springmvc.controller.EchoController"/>

        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="urlMap">
                <props>
                   <!--將url:/test.do映射到echoController處理-->
                    <prop key="/test.do">echoContorller</prop>
                </props>
            </property>
        </bean>

4 HandlerAdaptor

接口HandlerAdaptor如下:

// 參數(shù)handler即HandlerExecutionChain中的handler, supports判斷當(dāng)前adaptor是否適配handler
boolean supports(Object handler);

// 使用handler處理請求
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

4.1 HandlerAdaptor實(shí)現(xiàn)類

1. RequestMappingHandlerAdapter
它能適配RequestMappingHandlerMapping返回的handler吓坚,即MethodHandler的實(shí)例(它封裝了處理當(dāng)前請求url的bean以及更加具體method的信息)

RequestMappingHandlerAdapter會(huì)處理@PathVariable,@ModelAttribute等注解。

它會(huì)先完成使用了@ModelAttribute注解的方法的調(diào)用灯荧。然后調(diào)用url映射的具體方法的調(diào)用(調(diào)用前完成方法參數(shù)的綁定)礁击。

2. SimpleControllerHandlerAdapter
它能夠適配BeanNameUrlHandlerMappingSimpleUrlHandlerMapping返回的handler(也就是controller bean的實(shí)例)

5. HandlerInterceptor

在HandlerAdaptor # handle前后調(diào)用,也就是在用戶業(yè)務(wù)邏輯代碼前后提供預(yù)處理和后處理的能力逗载。

自定義interceptor需要實(shí)現(xiàn)接口HandlerInterceptor哆窿,然后在spring的xml文件中配置:

<bean id="myInterceptor" class="me.eric.springmvc.interceptors.MyInterceptor"/>
<mvc:interceptors>
            <mvc:interceptor>
                // 攔截這個(gè)地址
                <mvc:mapping path="/test.do"/>
               //不攔截這個(gè)地址
                <mvc:exclude-mapping path="/test2.do"
                <ref bean="myInterceptor"/>
            </mvc:interceptor>
</mvc:interceptors>

上面這種寫法,自定義攔截器myInterceptor會(huì)被包裝成MappedInterceptor厉斟,看看MappedInterceptor的成員就明白了:

// url匹配這些模式的攔截
private final String[] includePatterns;

// url匹配這些模式的不攔截
private final String[] excludePatterns;

// 真正的攔截器
private final HandlerInterceptor interceptor;

所以如果你不使用<mvc:interceptors>的形式定義攔截器挚躯,也可以直接使用MappedInterceptor作為bean,如下:

<bean class="org.springframework.web.servlet.handler.MappedInterceptor">
                <constructor-arg index="0">
                    <array>
                        <value>/test.do</value>
                    </array>
                </constructor-arg>
                <constructor-arg index="1">
                    <ref bean="timerInterceptor"/>
                </constructor-arg>
</bean>

上面不管哪種定義攔截器的方式擦秽,都會(huì)被RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping加載码荔。

要想單獨(dú)只被某個(gè)HandlerMapping加載,應(yīng)該做如下配置:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
       <property name="interceptors">
           <list>
                 <ref bean="myInterceptor"/>
           </list>
       </property>
</bean>

這樣myInterceptor這個(gè)攔截器只會(huì)被這個(gè)BeanNameUrlHandlerMapping使用到感挥。

注:如果按上面配置缩搅,同時(shí)xml文件里又存在<mvc:annotation-driven/>,那么可能發(fā)現(xiàn)不生效,原因是因?yàn)?code><mvc:annotation-driven/>會(huì)默認(rèn)加載一個(gè)BeanNameUrlHandlerMapping,而且它的優(yōu)先級是2(越小越高)触幼,而你在配置文件里的BeanNameUrlHandlerMapping默認(rèn)優(yōu)先級很低誉己,前面說到DispatcherServlet會(huì)對HandlerMapping排序,所以它默認(rèn)加載的總是會(huì)先使用域蜗。

一些參考

  1. spring mvc快速入門教程
  2. Spring MVC實(shí)踐
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市噪猾,隨后出現(xiàn)的幾起案子霉祸,更是在濱河造成了極大的恐慌,老刑警劉巖袱蜡,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丝蹭,死亡現(xiàn)場離奇詭異,居然都是意外死亡坪蚁,警方通過查閱死者的電腦和手機(jī)奔穿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敏晤,“玉大人贱田,你說我怎么就攤上這事∽炱ⅲ” “怎么了男摧?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵蔬墩,是天一觀的道長。 經(jīng)常有香客問我耗拓,道長拇颅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任乔询,我火速辦了婚禮樟插,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竿刁。我一直安慰自己黄锤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布们妥。 她就那樣靜靜地躺著猜扮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪监婶。 梳的紋絲不亂的頭發(fā)上旅赢,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機(jī)與錄音惑惶,去河邊找鬼煮盼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛带污,可吹牛的內(nèi)容都是我干的僵控。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鱼冀,長吁一口氣:“原來是場噩夢啊……” “哼报破!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起千绪,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤充易,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后荸型,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盹靴,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年瑞妇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稿静。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辕狰,死狀恐怖改备,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔓倍,我是刑警寧澤绍妨,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布润脸,位于F島的核電站,受9級特大地震影響他去,放射性物質(zhì)發(fā)生泄漏毙驯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一灾测、第九天 我趴在偏房一處隱蔽的房頂上張望爆价。 院中可真熱鬧,春花似錦媳搪、人聲如沸铭段。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽序愚。三九已至,卻和暖如春等限,著一層夾襖步出監(jiān)牢的瞬間爸吮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工望门, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留形娇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓筹误,卻偏偏與公主長得像桐早,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子厨剪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

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

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,815評論 6 342
  • 引言 一直以來都在使用Spring mvc哄酝,能夠熟練使用它的各種組件。但是祷膳,它一直像個(gè)黑盒一樣陶衅,我并不知道它內(nèi)部是...
    yoqu閱讀 909評論 0 24
  • 什么是Spring Spring是一個(gè)開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,464評論 1 133
  • Spring MVC一钾唬、什么是 Spring MVCSpring MVC 屬于 SpringFrameWork 的...
    任任任任師艷閱讀 3,381評論 0 32
  • 1、Spring MVC請求流程 (1)初始化:(對DispatcherServlet和ContextLoderL...
    拾壹北閱讀 1,948評論 0 12