SpringMVC源碼深度解析之SpringServletContainerInitializer原理分析

SpringMVC

對SpringMVC或者其它比較成熟的MVC框架而言,解決的問題無外乎如下幾點:html

  • 將web頁面的請求傳給服務器
  • 根據不一樣的請求處理不一樣的邏輯單元
  • 返回處理結果數據並跳轉至響應的頁面

Servlet與SpringMVC之間的關係

Spring的MVC是基於Servlet功能實現的,經過實現Servlet接口的DispatcherServlet來封裝其核心功能實現。java

快速搭建Servlet環境

<dependencies>
  <!-- 添加Servlet支持 -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
  </dependency>
</dependencies>

servlet線程是否安全?

不安全,構造函數只執行一次web

ServletContainerInitializer接口

在web容器啓動時會作一些初始化的工做,例如註冊servlet或者filtes等,servlet規範中經過ServletContainerInitializer實現此功能。spring

每一個框架要使用ServletContainerInitializer就必須在對應的jar包的META-INF/services 目錄建立一個名爲javax.servlet.ServletContainerInitializer的文件,文件內容指定具體的ServletContainerInitializer實現類。api

一、Servlet容器啓動會掃描,當前應用裏面每個jar包的ServletContainerInitializer的實現
二、提供ServletContainerInitializer的實現類;必須綁定在META-INF/services/javax.servlet.ServletContainerInitializer,文件的內容就是ServletContainerInitializer實現類的全類名;
tomcat

META-INF:tomcat默認就會去讀取的安全

案例演示:服務器

@HandlesTypes(value = MyHandlesType.class)//該註解聲明的類,會被注入到set中,若是沒有合適的類,set爲null
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * @param set 感興趣類型 也就是MyHandlesType 全部子類型
     * @param servletContext
     * @throws ServletException
     */
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        // 1.打印全部感興趣的類型
        for (Class<?> c : set) {
            System.out.println(c);
        }
       // 2.servletContext 手動註冊過濾器、servlet、監聽器
        ServletRegistration.Dynamic payServlet = servletContext.addServlet("payServlet", new PayServlet());
        payServlet.addMapping("/pay");
    }
}

基於註解方式構建SpringMVC框架

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>
  <!-- 添加Servlet支持 -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
  </dependency>
</dependencies>

springmvc是如何實現無web.xml配置,靠的就是ServletContainerInitializermvc

SpringServletContainerInitializer的做用:加載一些第三方的依賴信息oracle

@RestController
public class IndexController {

    @RequestMapping(value = "/",produces="text/html;charset=UTF-8")
    public String index() {
        return "success...";
    }
    /**
     * springmvc環境的時候須要配置那些東西?
     * SpringMVC啓動的時候如何實現沒有web.xml
     */
}
@Configuration
@ComponentScan("com.mayikt.controller")
@EnableWebMvc
public class SpringMvcConfig {
    //@EnableWebMvc 等於開啓SpringMVC註解方式
    //@Configuration xml
    // @ComponentScan("com.mayikt.controller") mvc 掃包範圍
    //DispatcherServlet
}
public class WebInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
       // 1.啓動SpringMVC 容器 類注入到Spring中
        AnnotationConfigWebApplicationContext app = new AnnotationConfigWebApplicationContext();// 啓動SpringMVC Web
        // 2.注入咱們的springmvc 的配置文件
        app.register(SpringMvcConfig.class);
        // 3. 將咱們的DispatcherServlet 注入到 serlvet容器中
        ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(app));
        // 4.填寫url路徑映射
        dynamic.addMapping("/");
        dynamic.setLoadOnStartup(1);// 優先級最高表示 最先被加載
    }
  // 基本配置已經ok呢? web.xml ? 使用WebApplicationInitializer 替代web.xml //爲何這個類WebInitializer 不須要註解呢? 可以自動的找到該類呢?
}

SpringServletContainerInitializer源碼分析

SpringServletContainerInitializer:

Servlet3.0引入的接口,用在web應用啓動時動態添加servlet、filter和listener

基於spi機制,META-INF/services/javax.servlet.ServletContainerInitializer文件中存放實現該接口的類,這些類會被容器調用;

只能使用在jar文件中,不能在web項目中使用。

進入:org.springframework.web.SpringServletContainerInitializer
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
...
}

SpringServletContainerInitializer 爲spring中實現ServletContainerInitializer接口的惟一類,該類主要是從容器獲取實現WebApplicationInitializer的類,而且按照次序javax.annotation.Priority)調用其onStartup方法

咱們定義的WebInitializer實現了WebApplicationInitializer接口

public class WebInitializer implements WebApplicationInitializer {
    public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
  ....
    }
}

原理:實現了WebApplicationInitializer 接口的類會被Spring初始化;因爲web項目中不能使用spi機制(未定位),因此若是須要動態添加servlet、filter和listener,就能夠實現該接口,交由spring初始化。

springmvc是如何替代web.xml

使用:SpringServletContainerInitializer 提供給SpringMVC實現初始化

debug:能夠看到WebApplicationInitializer子類有咱們自定義的WebInitializer

本文參考:

螞蟻課堂:http://www.mayikt.com/

文檔https://docs.oracle.com/javaee/7/api/javax/servlet/ServletContainerInitializer.html?is-external=true

相關文章
相關標籤/搜索