1、springBoot是如何去掉web.xml的配置的

1.1web.xml的作用

如下:是web.xml最基本配置,主要用来配置web的三大组件,listener监听器、servlet控制器、filter过滤器

1.1.1listener监听器

监听器的作用就是在ServletContextEvent事件的,当tomcat服务器启动,就会创建好ServletContext域对象,而当ServletContext创建好之后,listener就会监听到ServletContextEvent事件,就会执行contextInitialized()方法,来创建初始化springIOC容器

1.1.2filter过滤器

Filter也称之为过滤器,它是Servlet技术中最实用的技bai术,Web开发人员通过Filter技术,对web服务器管理的所有web资源。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter的生命周期

和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作

filter对象只会创建一次,init方法也只会执行一次。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

1.1.3servlet控制器

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
虽然,servlet可以配置多个,但是一般在springMVC的项目中只需要配置一个DispatcherServlet就可以了,DispatcherServlet创建好,初始化后会保存到容器中,只会创建一次,如果配置了1,则将在tomcat容器启动后就创建DispatcherServlet,并通过init方法初始化,并且HttpServet中的init()方法,进行初始化,并创建WebApplicationContext容器,这里创建的是springMVC容器,是springIOC的子容器
如果没有配置1,则将在第一次接收到请求时创建DispatcherServlet,并初始化,初始化过程是一样的

<!-- 制定spring配置文件位置 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--配置servlet核心 控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--配置全局参数,用来加载配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>


  <!--配置post请求中文编码过滤器-->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

1.2springBoot是怎么替代了web.xml

springBoot替代掉web.xml我认为根据springBoot项目的打包方式,运行方式的不同要分两种情况来说

1.2.1打war包通过外置tomcat运行

这种情况是遵循servlet3.0的ServletContainerInitializer机制,当tomcat启动就会去找到\spring-web-5.0.5.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer文件中配置的org.springframework.web.SpringServletContainerInitializer这个类,并执行其中的onStartup方法,在该方法中会遍历由@HandlesTypes注解加载进来的所有WebApplicationInitializer接口的子类型的类的类的集合,并遍历挨个执行它们的onStartup方法

  1. SpringBootServletInitializer的onStartup方法

这个类中主要构建的SpringApplication并调用了其run方法,接下来的步骤和打jar的方式步骤一样

  1. AbstractContextLoaderInitializer

这个类中注册了ContextLoaderListener
图片.png

  1. AbstractDispatcherServletInitializer

这个类中注册了DispatcherServlet,至此,web.xml中的配置servlet,listener就可以被替代了
图片.png
图片.png

  1. AbstractDispatcherHandlerInitializer

注册了dispatcherHandler
图片.png

1.2.2打jar包通过内置tomcat

这种情况下springBoot并没有完全遵循Servlet3.0的ServletContainerInitializer机制,断点调试可以发现,在整个springBoot项目启动运行期间,都不会进入到SpringServletContainerInitializer这个类中执行onStartup()方法,在这种使用内置tomcat打jar包的情况又是怎么来做的呢?
ServletContainerInitializer被一个叫ServletContextInitializer 替代,SpringServletContainerInitializer被TomcatStarter 替代。
当使用内嵌的 tomcat 时,你会发现 springboot 完全走了另一套初始化流程,完全没有使用前面提到的 SpringServletContainerInitializer,实际上一开始我在各种 ServletContainerInitializer 的实现类中打了断点,最终定位到,根本没有运行到 SpringServletContainerInitializer 内部,而是进入了 TomcatStarter 这个类中。TomcatStarter extends ServletContextInitializer
pringboot 这么做是有意而为之。springboot 考虑到了如下的问题,我们在使用 springboot 时,开发阶段一般都是使用内嵌 tomcat 容器,但部署时却存在两种选择:一种是打成 jar 包,使用 java -jar 的方式运行;另一种是打成 war 包,交给外置容器去运行。前者就会导致容器搜索算法出现问题,因为这是 jar 包的运行策略,不会按照 servlet3.0 的策略去加载 ServletContainerInitializer!最后作者还提供了一个替代选项:ServletContextInitializer,注意是 ServletContextInitializer!它和 ServletContainerInitializer 长得特别像,别搞混淆了,前者 ServletContextInitializer 是 org.springframework.boot.web.servlet.ServletContextInitializer,后者 ServletContainerInitializer 是 javax.servlet.ServletContainerInitializer,前文还提到 RegistrationBean 实现了 ServletContextInitializer 接口。

TomcatStarter中的ServletContextInitializer是关键

TomcatStarter 中的 org.springframework.boot.context.embedded.ServletContextInitializer 是 springboot 初始化 servlet,filter,listener 的关键。
TomcatStarter 的主要逻辑,它其实就是负责调用一系列 ServletContextInitializer 的 onStartup 方法,那么在 debug 中,ServletContextInitializer[] initializers 到底包含了哪些类呢?会不会有我们前面介绍的 RegisterBean 呢?
RegisterBean 并没有出现在 TomcatStarter 的 debug 信息中,initializers 只包含了三个类,其中只有第一个类看上去比较核心ServletWebServerApplicationContext,为了搞清楚 springboot 如何加载 filter servlet listener ,看来还得研究下 ServletWebServerApplicationContext的结构。
图片.png
断点调试会进入到ServletWebServerApplicationContext的selfInitialize()方法,
图片.png
selfInitialize()方法中,通过遍历ServletContextInitializer 的集合,调用它们的onStartup()进行web组件的注册,看下图可知,注册了一个Servlet合四个Fileter,而这个Servlet后续看到就是DispatcherServlet

这五个ServletContextInitializer都是RegisterationBean的子类,RegisterationBean的onStartup()方法,采用了模板方法模式,对不同的实现类web组件进行注册
图片.png
图片.png

重点:可以看到Servlet的注册,是通过TomcatStarter的onStartup方法,调用到ServletWebServerApplicationContext中的selfInitialize()方法,再调用到RegistrationBean的onStartup()方法,最终调用到ServletRegistrationBean中的addRegistration()方法进行servlet的注册,而且注册的就是DispatcherServlet,注册完成再通过configure()对Servlet进行配置,比如urlMapping、loadOnStartup等这个默认是-1,所以是懒加载
图片.png
图片.png
图片.png
过滤器Filter的注册,也是一样的原理
图片.png