SpringBoot中通過SpringBootServletInitializer如何實現容器初始化


在之前的《 SpringBoot之二:部署Spring Boot應用程序方式》章節有過一個實踐,需要啟動類繼承自SpringBootServletInitializer方可正常部署至常規tomcat下,其主要能夠起到web.xml的作用。下面通過源碼簡單解析為何其能夠替代web.xml。
 
本章概要
1、源碼分析如何實現SpringBootServletInitializer整個加載過程;
2、實現自定義WebApplicationInitializer配置加載;
3、實現自定義ServletContainerInitializer 配置加載;
 
示例代碼如下
1、首先web.xml主要配置各種servlet,filter,listener等,如常見的Log4jConfigListener、OpenSessionInViewFilter、CharacterEncodingFilter、DispatcherServlet等,此部分信息均是容器啟動時加載。
2、在springboot中我們從SpringBootServletInitializer源碼入手:
package org.springframework.boot.web.support;

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
    //...
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // Logger initialization is deferred in case a ordered
        // LogServletContextInitializer is being used
        this.logger = LogFactory.getLog(getClass());
        WebApplicationContext rootAppContext = createRootApplicationContext(
                servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                @Override
                public void contextInitialized(ServletContextEvent event) {
                    // no-op because the application context is already initialized
                }
            });
        }
        else {
            this.logger.debug("No ContextLoaderListener registered, as "
                    + "createRootApplicationContext() did not "
                    + "return an application context");
        }
    }
    //...
}
3、WebApplicationInitializer接口是通過spring的 SpringServletContainerInitializer中指定的用於啟動組件的。具體見《SpringMVC之五:自定義DispatcherServlet配置及配置額外的 servlets 和 filters》中的WebApplicationInitializer說明。
4、通過3中的說明可以很清楚的理解其服務啟動容器加載過程配置的裝載過程,在SpringServletContainerInitializer中可以發現所有WebApplicationInitializer實現類在執行onStartup方法前需要根據其注解@order值排序,下面自定義一個WebApplicationInitializer實現類:
package com.dxz.demo.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.web.WebApplicationInitializer;

@Order(1)
public class MyWebApplicationInitializer implements WebApplicationInitializer {
    private Logger logger = LoggerFactory.getLogger(MyWebApplicationInitializer.class);

    @Override
    public void onStartup(ServletContext paramServletContext) throws ServletException {
        logger.info("啟動加載自定義的MyWebApplicationInitializer");
        System.out.println("啟動加載自定義的MyWebApplicationInitializer");
    }

}
打成WAR包部署至常規tomcat下啟動服務驗證:
 
注:之前有專門講解如何裝載servlet、filter、listener的注解,且可以通過兩種不同的方式。那么第三種方式可以通過WebApplicationInitializer的實現類來進行裝載配置。但此方式僅限部署至常規容器下生效,采用jar方式應用內置容器啟動服務不加載
 
5、既然可以通過自定義的WebApplicationInitializer來實現常規容器啟動加載,那么我們是否可以直接自定義ServletContainerInitializer來實現啟動加載配置呢:
5.1、首先編寫一個待注冊的servlet:
package com.dxz.demo.config;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Servlet4 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet4");
        resp.setContentType("text/html");
        resp.getWriter().write("Servlet4");
    }

    @Override
    public void init() throws ServletException {
        super.init();
        System.out.println("Servlet4 loadOnStart");
    }

}
5.2、編寫實現ServletContainerInitializer的自定義實現類:
package com.dxz.demo.config;

import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyServletContainerInitializer implements ServletContainerInitializer {
    private Logger logger = LoggerFactory.getLogger(MyServletContainerInitializer.class);

    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        logger.info("啟動加載自定義的MyServletContainerInitializer");
        System.out.println("啟動加載自定義的MyServletContainerInitializer");
        ServletRegistration.Dynamic testServlet = servletContext.addServlet("servlet4",
                "com.shf.springboot.servlet.Servlet4");
        testServlet.setLoadOnStartup(1);
        testServlet.addMapping("/servlet4");
    }
}
5.3、對新增的servlet設置其請求路徑,同時打成WAR包部署至tomcat啟動服務,但請求 http://localhost:8080/SpringBoot1/servlet4卻失敗,此時發現需要了解servlet3對於ServletContainerInitializer 的加載機制是如何的,在官方有類似這樣的描述“該接口的實現必須聲明一個JAR資源放到程序中的META-INF/services下,並且記有該接口實現類的全路徑,才會被運行時(server)的查找機制或是其它特定機制找到”。那么我們先參考spring-web-4.3.2.RELEASE.jar中
我們可以將自定義的類配置到一個jar包下部署至WEB-INF\lib目錄下,
5.3.1、首先新建如下目錄結構的文件並填寫內容如下:
5.3.2、然后通過如下命令生成jar包
 
5.3.3、將myTest.jar放置WEB-INF\lib目錄下重啟服務,再次請求:
 
 
注:與4中實現WebApplicationInitializer一樣,該方式僅限於部署常規容器生效。故jar通過內置容器啟動的服務無法加載servlet4配置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM