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创建好,初始化后会保存到容器中,只会创建一次,如果配置了
如果没有配置
<!-- 制定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方法
- SpringBootServletInitializer的onStartup方法
这个类中主要构建的SpringApplication并调用了其run方法,接下来的步骤和打jar的方式步骤一样
- AbstractContextLoaderInitializer
这个类中注册了ContextLoaderListener
- AbstractDispatcherServletInitializer
这个类中注册了DispatcherServlet,至此,web.xml中的配置servlet,listener就可以被替代了

- AbstractDispatcherHandlerInitializer
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的结构。
断点调试会进入到ServletWebServerApplicationContext的selfInitialize()方法,
selfInitialize()方法中,通过遍历ServletContextInitializer 的集合,调用它们的onStartup()进行web组件的注册,看下图可知,注册了一个Servlet合四个Fileter,而这个Servlet后续看到就是DispatcherServlet
这五个ServletContextInitializer都是RegisterationBean的子类,RegisterationBean的onStartup()方法,采用了模板方法模式,对不同的实现类web组件进行注册

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


过滤器Filter的注册,也是一样的原理
