SpringBoot注册Servlet的3种方法

  1. 注解@WebServlet(“/hello”)
  2. ServletRegistrationBean

image.png

  1. 动态注册:通过实现ServletContextInitializer接口

image.png

ServletContextInitializer原理【Spring中注册Servlet】

其实ServletRegistrationBean 也是ServletContextInitializer
image.png

@WebServlet原理

ServletComponentRegisteringPostProcessor中通过WebServletHandler来处理带@WebServlet的类
image.png
其本质也是,构造一个ServletRegistrationBean(ServletContextInitializer)

三种方式最终都是ServletContextInitializer。


【servlet3.0】ServletContainerInitializer帮我们摆脱web.xml

image.png
项目中没有web.xml,取而代之是ServletContainerInitializer。
容器会通过SPI调用ServletContainerInitializer#onStartup,并且将@HandlesTypes中的类型作为参数传递到c中。
image.png

SpringMVC中如何注册DispatcherServlet的?【WebApplicationInitializer(SpringServletContainerInitializer)

上面我们知道ServletContainerInitializer#onStartup会在启动时运行。
SpringMVC实现的ServletContainerInitializer——SpringServletContainerInitializer

  • 在spring-web包中,通过spi被加载
  • 虽然被spi自动加载,但是WebApplicationInitializer需要我们自己定义。

image.png
SpringServletContainerInitializer中调用WebApplicationInitializer.onStartup()
-> 其中就有注册DispatcherServlet的WebApplicationInitializer【AbstractAnnotationConfigDispatcherServletInitializer】

AbstractAnnotationConfigDispatcherServletInitializer【WebApplicationInitializer】


SpringBoot如何容器初始化 - 图8
image.png

【总结】servlet3.0注册DispatcherServlet总结

  1. SPI调用ServletContainerInitializer#onStartup,根据@HandlesTypes(WebApplicationInitializer.class),将WebApplicationInitializer.class()传入onStartup()方法。
  2. AbstractDispatcherServletInitializer【WebApplicationInitializer】.onStartup() 中 registerDispatcherServlet(servletContext);注册DispatcherServlet。

    【war包运行】SpringBoot打war包部署方法

    步骤:

  3. 修改pom.xml文件将默认的jar方式改为war

  4. 继承org.springframework.boot.web.servlet.support.SpringBootServletInitializer(WebApplicationInitializer)
  1. @SpringBootApplication
  2. public class Application extends SpringBootServletInitializer {
  3. public static void main(String[] args) {
  4. SpringApplication.run(Application.class, args);
  5. }
  6. // @Override
  7. // protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
  8. // return application.sources(TestwarApplication.class);
  9. // }
  10. }

通常需要重写configure方法
当使用Application类继承的时候就不用,因为当判断source为空,会添加自身。
application.addPrimarySources(Collections.singleton(getClass()));

  • Deploy a Spring Boot WAR into a Tomcat Server

    为什么要继承SpringBootServletInitializer

    SpringBootServletInitializer 是一个抽象类(没有实现类),不能实例化。
    我们需要使用[servlet3.0标准的]SpringServletContainerInitializer(ServletContainerInitializer)来调用SpringBootServletInitializer(WebApplicationInitializer)中的onStartup() 方法
    在SpringBootServletInitializer.onStartup() 方法中会创建ApplicationContext等操作。

不用Application继承也行,可以另外写一个bean来继承,并注入到容器中

内嵌容器不支持ServletContainerInitializer,因此不能通过spi方式加载ServletContainerInitializer, 而是用TomcatStarter的onStartup,间接启动ServletContextInitializers,来达到ServletContainerInitializer的效果。

【好文】【好文】【好文】SpringBoot为什么没有web.xml了(转)- 深度好文 - EasyProgramming的个人空间 - OSCHINA

war启动简单总结

image.png

  • SpringBootServletInitializer【抽象类,需要Application继承】 implement WebApplicationInitializer 作为参数传入到了SpringServletContainerInitializer implements ServletContainerInitializer
  • SpringBootServletInitializer 启动了ServletWebServerApplicationContext
  • ServletWebServerApplicationContext.onRefresh() 执行了ServletWebServerApplicationContext#ServletContextInitializer
  • ServletWebServerApplicationContext#ServletContextInitializer 执行了一系列ServletContextInitializer(从BeanFactory中获得)
  • DispatcherServletRegistrationBean(ServletContextInitializer)被执行注册Servlet

【jar运行】SpringBoot如何注册DispatcherServlet的?

  • 内嵌容器,所有的BeanDefinition都在applicationContext中了,用的话直接getBean即可(从applicationContext中获取servlet、ServletContextInitializer))。

ServletWebServerApplicationContext重写onRefresh()方法。
image.png
最终调用ServletContextInitializer.onStartup()方法
其中有注册DispatcherServlet的ServletContextInitializer就是DispatcherServletRegistrationBean。
RegistrationBean#onStartup(ServletContext servletContext)注册Servlet到ServletContext
image.png
image.png

有了servlet,applicationContext何时注入到servlet中呢?

是实例化FrameworkServlet时注入。image.png

DispatcherServlet如何初始化SpringMVC组件的?

  1. FrameworkServlet初始化期间构造ApplicationContext,并向其中添加ContextRefreshListener。
  2. 容器refresh完成会触发事件调用ContextRefreshListener。

FrameworkServlet#configureAndRefreshWebApplicationContext
image.png
image.png

  • springboot是将applicationContext传入FrameworkServlet中(通过aware在构造FrameworkServlet时传入)。
  • springboot不会进入createWebApplicationContext()方法。所以不会添加listener。所以,下面的onRefresh()被调用,初始化springmvc组件。

  • SpringMVC调用createWebApplicationContext()。通过Listener来调用onRefresh()。

image.png

  1. 在DispatcherServlet#onRefresh中初始化SpringMVC组件

image.png
image.png
默认组件都定义在DispatcherServlet.properties
image.png

内嵌tomcat的运行

在finishRefresh()中执行了tomcat.start()