在之前的《使用jsp作为视图模板&常规部署》章节有过一个实践,需要启动类继承自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源码入手:

  1. public abstract class SpringBootServletInitializer implements WebApplicationInitializer{
  2. ..................
  3. public void onStartup(ServletContext servletContext) throws ServletException {
  4. this.logger = LogFactory.getLog(super.getClass());
  5. WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
  6. if (rootAppContext != null) {
  7. servletContext.addListener(new ContextLoaderListener(rootAppContext) {
  8. public void contextInitialized(ServletContextEvent event) {
  9. }
  10. });
  11. } else
  12. this.logger.debug(
  13. "No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
  14. }
  15. ....................
  16. }

可以先关注此类实现了WebApplicationInitializer,那么实现了此接口又如何呢?

3、下面继续关注一个spring源码:

  1. <code class="language-java"><span style="font-size:14px;">@HandlesTypes({ WebApplicationInitializer.class })
  2. public class SpringServletContainerInitializer implements <span style="background-color:rgb(255,255,255);">ServletContainerInitializer </span>{
  3. public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
  4. throws ServletException {
  5. List initializers = new LinkedList();
  6. if (webAppInitializerClasses != null) {
  7. for (Class waiClass : webAppInitializerClasses) {
  8. if ((!(waiClass.isInterface())) && (!(Modifier.isAbstract(waiClass.getModifiers())))
  9. && (WebApplicationInitializer.class.isAssignableFrom(waiClass))) {
  10. try {
  11. initializers.add((WebApplicationInitializer) waiClass.newInstance());
  12. } catch (Throwable ex) {
  13. throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
  14. }
  15. }
  16. }
  17. }
  18. if (initializers.isEmpty()) {
  19. servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
  20. return;
  21. }
  22. servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
  23. AnnotationAwareOrderComparator.sort(initializers);
  24. for (WebApplicationInitializer initializer : initializers)
  25. initializer.onStartup(servletContext);
  26. }
  27. }</span></code>

  1. @HandlesTypes({ WebApplicationInitializer.class })
  2. public class SpringServletContainerInitializer implements ServletContainerInitializer {
  3. public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
  4. throws ServletException {
  5. List initializers = new LinkedList();
  6. if (webAppInitializerClasses != null) {
  7. for (Class waiClass : webAppInitializerClasses) {
  8. if ((!(waiClass.isInterface())) && (!(Modifier.isAbstract(waiClass.getModifiers())))
  9. && (WebApplicationInitializer.class.isAssignableFrom(waiClass))) {
  10. try {
  11. initializers.add((WebApplicationInitializer) waiClass.newInstance());
  12. } catch (Throwable ex) {
  13. throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
  14. }
  15. }
  16. }
  17. }
  18. if (initializers.isEmpty()) {
  19. servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
  20. return;
  21. }
  22. servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
  23. AnnotationAwareOrderComparator.sort(initializers);
  24. for (WebApplicationInitializer initializer : initializers)
  25. initializer.onStartup(servletContext);
  26. }
  27. }

4、继续关注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。

5、通过4中的说明可以很清楚的理解其服务启动容器加载过程配置的装载过程,在SpringServletContainerInitializer中可以发现所有WebApplicationInitializer实现类在执行onStartup方法前需要根据其注解@order值排序,下面自定义一个WebApplicationInitializer实现类:

  1. package com.shf.springboot.config;
  2. import javax.servlet.ServletContext;
  3. import javax.servlet.ServletException;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.core.annotation.Order;
  7. import org.springframework.web.WebApplicationInitializer;
  8. import com.shf.springboot.runner.MyStartupRunner1;
  9. @Order(1)
  10. public class MyWebApplicationInitializer implements WebApplicationInitializer {
  11. private Logger logger=LoggerFactory.getLogger(MyStartupRunner1.class);
  12. @Override
  13. public void onStartup(ServletContext paramServletContext) throws ServletException {
  14. logger.info("启动加载自定义的MyWebApplicationInitializer");
  15. System.out.println("启动加载自定义的MyWebApplicationInitializer");
  16. }
  17. }
打成WAR包部署至常规tomcat下启动服务验证:


注:之前有专门讲解如何装载servlet、filter、listener的注解,且可以通过两种不同的方式。那么第三种方式可以通过WebApplicationInitializer的实现类来进行装载配置。但此方式仅限部署至常规容器下生效,采用jar方式应用内置容器启动服务不加载

6、既然可以通过自定义的WebApplicationInitializer来实现常规容器启动加载,那么我们是否可以直接自定义ServletContainerInitializer来实现启动加载配置呢:
6.1、首先编写一个待注册的servlet:

  1. package com.shf.springboot.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletContext;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import org.springframework.boot.web.servlet.ServletContextInitializer;
  10. public class Servlet4 extends HttpServlet {
  11. private static final long serialVersionUID = -4186518845701003231L;
  12. @Override
  13. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  14. System.out.println("Servlet4");
  15. resp.setContentType("text/html");
  16. resp.getWriter().write("Servlet4");
  17. }
  18. @Override
  19. public void init() throws ServletException {
  20. super.init();
  21. System.out.println("Servlet4 loadOnStart");
  22. }
  23. }
6.2、编写实现ServletContainerInitializer的自定义实现类:

  1. package com.shf.springboot.config;
  2. import java.util.Set;
  3. import javax.servlet.ServletContainerInitializer;
  4. import javax.servlet.ServletContext;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.ServletRegistration;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. public class MyServletContainerInitializer implements ServletContainerInitializer {
  10. private Logger logger=LoggerFactory.getLogger(MyServletContainerInitializer.class);
  11. @Override
  12. public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
  13. logger.info("启动加载自定义的MyServletContainerInitializer");
  14. System.out.println("启动加载自定义的MyServletContainerInitializer");
  15. ServletRegistration.Dynamic testServlet=servletContext.addServlet("servlet4","com.shf.springboot.servlet.Servlet4");
  16. testServlet.setLoadOnStartup(1);
  17. testServlet.addMapping("/servlet4");
  18. }
  19. }

6.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目录下,
6.3.1、首先新建如下目录结构的文件并填写内容如下:

6.3.2、然后通过如下命令生成jar包

6.3.3、将myTest.jar放置WEB-INF\lib目录下重启服务,再次请求:


注:与5中实现WebApplicationInitializer一样,该方式仅限于部署常规容器生效。故jar通过内置容器启动的服务无法加载servlet4配置。

常规容器下SpringBootServletInitializer如何实现web.xml作用解析的更多相关文章

  1. 转 web项目中的web.xml元素解析

    转 web项目中的web.xml元素解析 发表于1年前(2014-11-26 15:45)   阅读(497) | 评论(0) 16人收藏此文章, 我要收藏 赞0 上海源创会5月15日与你相约[玫瑰里 ...

  2. javaweb项目中关于配置文件web.xml的解析

    一..启动tomcat,加载项目中的web.xml文件,创建servercontext上下文对象. 可以通过servercontext对象在应用中获取web.xml文件中的值. web应用加载的顺序与 ...

  3. 使用Eclipse创建Web项目时WEB-INF下找不到web.xml问题详解

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/yjrguxing/article/deta ...

  4. ssm web.xml配置解析

    以下为web.xml的配置<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi=& ...

  5. ssm web.xml文件解析

    转   以下为web.xml的配置<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:x ...

  6. javaweb学习总结十七(web应用组织结构、web.xml作用以及配置虚拟主机搭建网站)

    一:web应用组织结构 1:web应用组成结构 2:安装web组成机构手动创建一个web应用程序目录 a:在webapps下创建目录web b:在web目录下创建html.jsp.css.js.WEB ...

  7. struts2中struts.xml和web.xml文件解析及工作原理

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp ...

  8. JSF技术web.xml配置解析

    对Java tutorial-examples中jsf hell1的web.xml配置文件的解析 <?xml version="1.0" encoding="UTF ...

  9. hello1 web项目中web.xml作用分析

    该web.xml文件包含Facelets应用程序所需的几个元素.使用NetBeans IDE创建应用程序时,将自动创建以下所有内容. 指定项目阶段的上下文参数: <context-param&g ...

随机推荐

  1. Android中的Parcelable接口和Serializable使用方法和差别

    Parcelable接口: Interface for classes whose instances can be written to and restored from a Parcel. Cl ...

  2. Android---- 获取当前应用的版本号和当前android系统的版本号

    Android 应用程序获得版本号 我们可以在AndroidManifest.xml中设置程序的版本号等,如android:versionName="1.0",那如果想在代码中获取 ...

  3. Rick's RoTs -- Rules of Thumb for MySQL--转载

    原文地址:http://mysql.rjweb.org/doc.php/ricksrots Brought to you by Rick James Here are 160+ tips, trick ...

  4. Android EditText回车不换行

    有时候我们需要在EditText组件输入完直接点回车进入 回车点击事件中处理相关业务,总是换行很伤脑筋,索性网上搜了一个办法,很实用,在这里记下来. 首先获取组件id: edt_city= (Edit ...

  5. AJAX有关的请求协议及HTTP报文

    URI:统一资源标识符 URI=URL+URNURL:统一资源定位符URN:统一资源名称 上边的图片编号对应下边的编号说明: 1.HTTP(占90%市场)/HTTPS/FTP 传输协议(可以理解为快递 ...

  6. mootools常用特性和示例(基础篇2)

    接着上一篇:mootools常用特性和示例(基础篇1) 1.表单操作 html: <form id="myForm" action="submit.php" ...

  7. 如何在 Linux 中统计一个进程的线程数

    编译自:http://ask.xmodulo.com/number-of-threads-process-linux.html作者: Dan Nanni原创:LCTT https://linux.cn ...

  8. putty-gns3

    hcl-cloud用的就是这个putty http://forum.gns3.net/topic5016.html File comment: Compiled PuTTY 0.62 for wind ...

  9. Spinlock implementation in ARM architecture

    Spinlock implementation in ARM architecture   SEV and WFE are the main instructions used for impleme ...

  10. Facebook Hacker Cup 2015 Round 1--Winning at Sports(动态规划)

    原题:pid=688426044611322&round=344496159068801">https://www.facebook.com/hackercup/problem ...