在以前的《使用jsp做爲視圖模板&常規部署》章節有過一個實踐,須要啓動類繼承自SpringBootServletInitializer方可正常部署至常規tomcat下,其主要可以起到web.xml的做用。下面經過源碼簡單解析爲什麼其可以替代web.xml。html
本章概要java
一、源碼分析如何實現SpringBootServletInitializer整個加載過程;web
二、實現自定義WebApplicationInitializer配置加載;spring
三、實現自定義ServletContainerInitializer 配置加載;tomcat
示例代碼以下springboot
一、首先web.xml主要配置各類servlet,filter,listener等,如常見的Log4jConfigListener、OpenSessionInViewFilter、CharacterEncodingFilter、DispatcherServlet等,此部分信息均是容器啓動時加載。app
二、在springboot中咱們從SpringBootServletInitializer源碼入手:jsp
public abstract class SpringBootServletInitializer implements WebApplicationInitializer{ .................. public void onStartup(ServletContext servletContext) throws ServletException { this.logger = LogFactory.getLog(super.getClass()); WebApplicationContext rootAppContext = createRootApplicationContext(servletContext); if (rootAppContext != null) { servletContext.addListener(new ContextLoaderListener(rootAppContext) { public void contextInitialized(ServletContextEvent event) { } }); } else this.logger.debug( "No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context"); } .................... }
能夠先關注此類實現了WebApplicationInitializer,那麼實現了此接口又如何呢?ide
三、下面繼續關注一個spring源碼:源碼分析
<code class="language-java"><span style="font-size:14px;">@HandlesTypes({ WebApplicationInitializer.class }) public class SpringServletContainerInitializer implements <span style="background-color:rgb(255,255,255);">ServletContainerInitializer </span>{ public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List initializers = new LinkedList(); if (webAppInitializerClasses != null) { for (Class waiClass : webAppInitializerClasses) { if ((!(waiClass.isInterface())) && (!(Modifier.isAbstract(waiClass.getModifiers()))) && (WebApplicationInitializer.class.isAssignableFrom(waiClass))) { try { initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) initializer.onStartup(servletContext); } }</span></code>
四、繼續關注3中紅色標示部分,此時咱們先來看ServletContainerInitializer的做用,其主要就是在啓動容器時負責加載相關配置:
public abstract interface ServletContainerInitializer { public abstract void onStartup(Set<Class<?>> paramSet, ServletContext paramServletContext) throws ServletException; }
容器啓動時會自動掃描當前服務中ServletContainerInitializer的實現類,並調用其onStartup方法,其參數Set<Class<?>> c,可經過在實現類上聲明註解javax.servlet.annotation.HandlesTypes(WebApplicationInitializer.class)註解自動注入,@HandlesTypes會自動掃描項目中全部的WebApplicationInitializer.class的實現類,並將其所有注入Set。
五、經過4中的說明能夠很清楚的理解其服務啓動容器加載過程配置的裝載過程,在SpringServletContainerInitializer中能夠發現全部WebApplicationInitializer實現類在執行onStartup方法前須要根據其註解@order值排序,下面自定義一個WebApplicationInitializer實現類:
package com.shf.springboot.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; import com.shf.springboot.runner.MyStartupRunner1; @Order(1) public class MyWebApplicationInitializer implements WebApplicationInitializer { private Logger logger=LoggerFactory.getLogger(MyStartupRunner1.class); @Override public void onStartup(ServletContext paramServletContext) throws ServletException { logger.info("啓動加載自定義的MyWebApplicationInitializer"); System.out.println("啓動加載自定義的MyWebApplicationInitializer"); } }
打成WAR包部署至常規tomcat下啓動服務驗證:
注:以前有專門講解如何裝載servlet、filter、listener的註解,且能夠經過兩種不一樣的方式。那麼第三種方式能夠經過WebApplicationInitializer的實現類來進行裝載配置。但此方式僅限部署至常規容器下生效,採用jar方式應用內置容器啓動服務不加載。
六、既然能夠經過自定義的WebApplicationInitializer來實現常規容器啓動加載,那麼咱們是否能夠直接自定義ServletContainerInitializer來實現啓動加載配置呢:
6.一、首先編寫一個待註冊的servlet:
package com.shf.springboot.servlet; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.boot.web.servlet.ServletContextInitializer; public class Servlet4 extends HttpServlet { private static final long serialVersionUID = -4186518845701003231L; @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"); } }
6.二、編寫實現ServletContainerInitializer的自定義實現類:
package com.shf.springboot.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"); } }
6.三、對新增的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目錄下,
6.3.一、首先新建以下目錄結構的文件並填寫內容以下:
6.3.二、而後經過以下命令生成jar包
6.3.三、將myTest.jar放置WEB-INF\lib目錄下重啓服務,再次請求:
注:與5中實現WebApplicationInitializer同樣,該方式僅限於部署常規容器生效。故jar經過內置容器啓動的服務沒法加載servlet4配置。