1、首先来分析springBoot项目的启动方式

1.1方式一、内嵌Tomcat

默认的application启动,在创建项目时自动生成application启动类,直接run执行即可。
也就是启动主程序,直接通过SpringApplication.run()来启动项目,这样不要求web项目打war包,直接jar包就可以运行,这也是当前常用的方式,这种启动流程的分析,详见《springBoot是如何内嵌Tomcat的》文章中。
在这种情况下,springBoot并没有使用servlet3.0的ServletContainerInitializer机制,也就是并不会去扫描
\spring-web-5.0.5.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer文件中配置的SpringServletContainerInitializer这个类,也就没有加载WebApplicationInitializer的所有子类,没有执行其中的onStartup()方法,所以也就没有使用servlet3.0的ServletContainerInitializer机制。

  1. /**
  2. * @author: Luck-zb
  3. * description:来测试一下FactoryBean
  4. * Date:2021/2/24 - 17:18
  5. */
  6. @SpringBootApplication
  7. public class TestApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(TestApplication.class, args);
  10. }

1.2方式二、使用外置的tomcat启动

1.2.1 默认的启动类要继承SpringBootServletInitiailzer类,并复写configure()方法。

  1. @SpringBootApplication
  2. public class FileuploadApplication extends SpringBootServletInitializer {
  3. public static void main(String[] args) {
  4. SpringApplication.run(FileuploadApplication.class, args);
  5. }
  6. @Override
  7. protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
  8. return super.configure(builder);
  9. }

1.2.2.添加本地tomcat并进行配置

SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,并最终调用SpringApplication的run方法的过程。spring boot就是为了简化开发的,也就是用注解的方式取代了传统的xml配置。SpringBootServletInitializer就是原有的web.xml文件的替代。
使用了嵌入式Servlet,默认是不支持jsp。
SpringBootServletInitializer 可以使用外部的Servlet容器,使用步骤:
1.必须创建war项目,需要创建好web项目的目录。
2.嵌入式Tomcat依赖scope指定provided。
3.编写SpringBootServletInitializer类子类,并重写configure方法。
4.启动服务器。
图片.png
图片.png
5运行tomcat测试
提供了自定义的实现ServletContainerInitializer接口的类,重写里的onStartup()方法,并且还提供了自定义的实现了WebApplicationInitializer接口的类,同样重写了onStartup()方法
图片.png
图片.png
图片.png
可以看到如下的运行结果,我们自定义的,包括spring提供的,都执行了onStartup()方法
图片.png

1.3ar包和war包启动区别

jar包:执行SpringBootApplication的run方法,启动IOC容器,然后创建嵌入式Servlet容器,Tomcat启动时创建,而Tomcat是在调用容器的onRefresh()方法时才创建,所以是先IOC,再Servlet容器
war包: 先是启动Servlet服务器,服务器启动Springboot应用(springBootServletInitizer),然后启动IOC容器,所以是先servlet容器,再IOC容器
SpringBootServletInitializer实例执行onStartup方法的时候会通过createRootApplicationContext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建IOC容器并返回,只是以war包形式的应用在创建IOC容器过程中,不再创建Servlet容器了。

1.4外置tomcat启动流分析

根据servlet3.0机制,tomcat启动就会找到SpringServletContainerInitializer这个类,并执行onStartup方法,这个方法中通过HandlesTypes注解将所有WebApplicationInitializer的实现类的集合传了进来,挨个遍历,执行它们的onStartup方法,当然也包括了SpringBootServletInitializer,接下来重点分析SpringBootServletInitializer中干了啥事。

1.4.1首先onStartup方法会去调用创建跟容器的方法

图片.png

1.4.2createRootApplicationContext创建跟容器的方法

  1. protected WebApplicationContext createRootApplicationContext(
  2. ServletContext servletContext) {
  3. SpringApplicationBuilder builder = createSpringApplicationBuilder();
  4. StandardServletEnvironment environment = new StandardServletEnvironment();
  5. // 首先初始化环境信息,并且servletContext已经创建好
  6. environment.initPropertySources(servletContext, null);
  7. builder.environment(environment);
  8. builder.main(getClass());
  9. ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
  10. if (parent != null) {
  11. this.logger.info("Root context already created (using as parent).");
  12. servletContext.setAttribute(
  13. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
  14. builder.initializers(new ParentContextApplicationContextInitializer(parent));
  15. }
  16. builder.initializers(
  17. new ServletContextApplicationContextInitializer(servletContext));
  18. builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
  19. builder = configure(builder);
  20. // 构建了一个SpringApplication
  21. SpringApplication application = builder.build();
  22. if (application.getAllSources().isEmpty() && AnnotationUtils
  23. .findAnnotation(getClass(), Configuration.class) != null) {
  24. application.addPrimarySources(Collections.singleton(getClass()));
  25. }
  26. Assert.state(!application.getAllSources().isEmpty(),
  27. "No SpringApplication sources have been defined. Either override the "
  28. + "configure method or add an @Configuration annotation");
  29. // Ensure error pages are registered
  30. if (this.registerErrorPageFilter) {
  31. application.addPrimarySources(
  32. Collections.singleton(ErrorPageFilterConfiguration.class));
  33. }
  34. // 调用这个方法
  35. return run(application);
  36. }

1.4.3run(SpringApplication application)

图片.png
这个方法就还是回到了SpringApplication.run(),后续的流程和jar包方式启动main方法一样了,只不过容器的创建可能有些差别,比如,将不在创建servlet容器等