俯視SpringMvc

認識SpringMvc

作爲web的開發人員,你是否知道自己寫的web代碼本質是什麼,爲什麼我們開發的java應用能在tomcat上應用(或其它java服務器)。因爲我們的代碼是根據servlet規範來開發的,它制定了java中處理請求的標準。繼承了servlet我們需要實現它的幾個方法,常見的就是init、service、destroy等,而一個tomcat服務它是有很多層次結構的,但它的每一層基本都實現了init等方法,當我們的應用部署到tomcat裏面時,tomcat的頂層容器就逐一的調用下一層的init方法來觸發子容器,到了最後一層就能觸發到了我們的servlet程序,所以我們的應用就能夠被訪問了。
SpringMvc是一個很成熟並被廣泛應用的開發框架,其實它的本質也是servlet,只是它幫我們封裝好了,讓我們可以根據自己的業務更靈活的開發,而不用生硬的遵守servlet的規範。

1、servlet

servlet它是一套規範,所以它主要的工作就是定義一些接口,每一個接口解決不同的邏輯功能,但是具體的實現要取決於實現他的人。
servlet3.1.0中servlet的源碼如下:

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

init方法會在容器(tomcat)啓動時被調用,但是我們一個應用有非常多的servlet,所以當然不是每個servlet的init都會在啓動時被調用,我們在web.xml配置servlet時會看到一個(load-on-start)的設置。

  1. load-on-startup元素標記容器是否在啓動的時候就加載這個servlet(實例化並調用其init()方法)。
  2. 它的值必須是一個整數,表示servlet應該被載入的順序
  3. 當值爲0或者大於0時,表示容器在應用啓動時就加載並初始化這個 servlet;
  4. 當值小於0或者沒有指定時,則表示容器在該servlet被選擇時纔會去加載。
  5. 正數的值越小,該servlet的優先級越高,應用啓動時就越先加載。
  6. 當值相同時,容器就會自己選擇順序來加載。

    一般我們寫一個servlet會繼承HttpServlet,從下面的源碼中我們又可以看到,HttpServlet 繼承了GenericServlet ,而GenericServlet 實現了Servlet和ServletConfig接口。(我是覺得看一個框架的結構樹,還不如應用打開,一層層的跟進去看看它的代碼實現可能映像更深刻)

public abstract class HttpServlet extends GenericServlet {
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
  • ServletConfig:顧名思義存放的是servlet配置的。
  • Serializable :將該java對象序列化(也就是將它在內存中的各個狀態記錄下來,保存成文件或者通過網絡傳到別的系統,其它應用讀出這個序列話對象時就能還原成這個對象)
  • GenericServlet :主要做了3件事,簡單講就是讓我們更加方便的使用servlet。
    • 實現了ServletConfig接口
    • 提供了無參的init()方法
    • 提供了log方法

2、tomcat

Tomcat中最頂層的容器是server,它代表整個服務器,server中至少包含一個service,用於提供具體的服務。service主要包含兩部分:connector和container。connect中主要處理鏈接相關的事情,並提供socket與request、response的轉換。container用於封裝和管理servlet,以及具體處理request的請求。
tomcat服務器結構(圖片來自互聯網)

server中提供了init和start方法,當容器啓動後他們分別循環調用了每個init方法和start方法來啓動說有service,service中又調用了子容器的init和start方法。以此遞推進去就能調到servlet中的init方法了。

環境搭建

開發換件用IDEA,並使用maven架構。(maven如果不清楚可以自己額外去了解)

新建工程

File –》 new Module –》 Maven (注:Idea中的module就是項目,等同於eclipse的project)
這裏寫圖片描述

項目的簡單工程結構如下(用腳手架自動生成的工程要是沒有相應的結構可以根據自己的需要新建)
工程結構

配置環境

1.首先要在pom.xml中加入springmvc 和 servlet的依賴

 <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.1.5.RELEASE</version>
    </dependency>
  </dependencies>

2.web.xml中配置servlet

    <!-- spring mvc配置開始  -->
    <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-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

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

contextConfigLocation指定了springmvc配置文件的具體位置,如果沒有指定就默認使用WEB-INFO/[ServletName]-servlet.xml文件。
3.創建SpringMvc的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="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.xsd
   http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

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

    <!-- 聲明viewResolver -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="view/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

<mvc:annotation-driven/>會幫我們做一些自動註冊組件的事情。

<context:component-scan base-package="com.sunshine.springmvc.web"/>指定了制定了要掃描配置的類的地方。

在這一串配置中最令人難懂的應該就是頭部的一大串聲明瞭。這一串稱之爲Schema-based XML的聲明,引入了這個聲明就可以簡單的在下面定義我們的組件。如:mvc:annotation、context:component-scan

其實這2個和下面通過<bean> 標籤的作用是相同的:對SpringMVC中的組件進行聲明,指定組件的行爲方式。
- 基於Schema-based XML的配置定義模式
- 基於Traditional XML的配置定義模式
毫無疑問第一種的配置是比較簡單的,第二種要一個個servlet去定義,而第二種是根據整個邏輯功能引入的,像mvc:annotation-driven,這個用來註冊組件的功能裏就註冊了好幾個servlet。
有關Schema-based XML的概念,可以參考Spring官方的reference:
Appendix C. XML Schema-based configuration

組件介紹

srpingmvc繼承結構圖

從上圖我們可以看到springmv組要的結構是HttpServletBean、FrameworkServlet、DispatcherServlet,其它的是上面講的java servlet api中的結構。

  • HttpServletBean
    • 將servlet中配置的參數設置到DispacherServlet的相關屬性
    • 調用模板方法initServletBean,子類就可以通過這個方法初始化
  • FrameworkServlet
    • 初始化了WebApplication
      • 獲取了spring的根容器rootContext
      • 設置webApplicationContext並根據情況調用onRefresh方法。
      • 將webApplicationContext設置到ServletContext中
  • DispatcherServlet
    上面的onRefresh方法是DispatcherServlet的入口方法。onRefresh中簡單調用了initStrategies,在initStrategies中調用了9個初始化方法:
    protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }

1. HanderMapping

根據request找到相應的處理器hander 和 Interceptors

2. HandlerAdapter

handler是具體處理事情的,HandlerAdapter就是使用handler來完成這件事的。adapter是適配器的意思,可能到adapter的請求各種各樣,但他們要完成的都是同一個handler這件事。

3. HandlerExceptionResolver

只用於對請求做處理過程中產生的異常,而渲染環節產生的異常不歸它管。

4. ViewResolver

用來將String類型的視圖名和locale(國際化用的有默認值)解析爲view類型的視圖。

5. RequestToViewNameTranslator

ViewResolver是根據viewname查找view,當viewname沒有的話,就要從request中獲取了。

6. LocaleResolver

用於從request中解析出locale,提供給ViewResolver作爲參數用。

7. ThemeResolver

解析主體的。

8. MultipartResolver

用於處理上傳請求。

9. FlashMapManager

主要用於redirect中傳遞參數的

總結(處理流程)

  1. 請求發送到服務器,服務器分配一個socket線程跟它鏈接,接着創建出request和response,接着交給對應的servlert處理。
  2. servlet中的請求首先會被httpservlet處理,將servletrequest和servletresponse轉換爲httpservletrequest和httpservletresponse,然後調用service方法。
  3. 接着請求就來到了springmvc,首先到達的是FrameworkServlet,做了一些初始化動作後講請求交給DispatcherServlet
  4. DispatcherServlet是SpringMvc的核心內容,它需要將請求交給對應的hander處理,但是hander有很多個,它需要先通過handerMapping,找到對應的hander.
  5. 找到了hander後,DispatcherServlet將它交給具體的handerAdapter處理。
  6. 處理完業務邏輯後會調用modelAndView對結果進行處理。
  7. 之後交給viewResovler,根據數據(model)和試圖(view)渲染成頁面返回。

以上是自己學習之後的總結,可能有些地方自己的理解有偏誤,歡迎指正,僅供參考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章