在之前的《使用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. 洛谷——P1179 数字统计

    https://www.luogu.org/problem/show?pid=1179 题目描述 请统计某个给定范围[L, R]的所有整数中,数字 2 出现的次数. 比如给定范围[2, 22],数字 ...

  2. 硬件——nrf51822第一篇,GPIO的使用

    未完,待续...... 本实现是基于一个开发箱,包括:综合应用开发系统主板XT-EDU-AK   1套: 手持终端系统 XT-EDU-HK 1套: GPIO操作 工程: 这是一个关于流水灯的程序: 我 ...

  3. JS学习笔记 - 运动 - 淘宝轮播图

    <script> window.onload=function () { var oDiv=document.getElementById('play'); var aBtn=oDiv.g ...

  4. python3 购物车小程序,余额写入文件保存

    python3 购物车小程序,余额写入文件保存 #!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Hiuhung Wan goods = ( ...

  5. mysql三种带事务批量插入

    原文:mysql三种带事务批量插入 c#之mysql三种带事务批量插入 前言 对于像我这样的业务程序员开发一些表单内容是家常便饭的事情,说道表单 我们都避免不了多行内容的提交,多行内容保存,自然要用到 ...

  6. Oracle 11gR2 静默安装奇怪错误

    在静默安装Oracle 11gR2 的时候发现的奇怪错误,有点摸不着头脑 【步骤一】配置静默文件只安装软件 #--------------------------------------------- ...

  7. arm-linux-gcc: Command not found

    老是提示arm-linux-gcc找不到,但是确实是装好了,其实是权限的问题,Ubuntu没有root权限,刚开始用碰到很多麻烦,查了好多资料,终于把arm-linux-gcc: Command no ...

  8. EJBCA在Linux上的安装

    在windows上安装为了測试用,装在linux服务器上的由于CN用的ip须要重装.....又是折腾一番,以下介绍一些须要注意的地方 一.所需文件 准备的内容就不说了,參考我的上上篇<EJBCA ...

  9. google analytics是什么(免费的网站流量分析服务:比如分析有多少个人来了你的网站,告诉你怎么样才能在网站上面实现最大收益。)

    google analytics是什么(免费的网站流量分析服务:比如分析有多少个人来了你的网站,告诉你怎么样才能在网站上面实现最大收益.) 一.总结 免费的网站流量分析服务:比如分析有多少个人来了你的 ...

  10. 钢琴 - steinway

    http://www.wangyanpiano.com/bbs/thread-104723-1-1.html Poston 波士顿钢琴    三角钢琴    GP-156    黑色抛光    178 ...