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机制。
/*** @author: Luck-zb* description:来测试一下FactoryBean* Date:2021/2/24 - 17:18*/@SpringBootApplicationpublic class TestApplication {public static void main(String[] args) {SpringApplication.run(TestApplication.class, args);}
1.2方式二、使用外置的tomcat启动
1.2.1 默认的启动类要继承SpringBootServletInitiailzer类,并复写configure()方法。
@SpringBootApplicationpublic class FileuploadApplication extends SpringBootServletInitializer {public static void main(String[] args) {SpringApplication.run(FileuploadApplication.class, args);}@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return super.configure(builder);}
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.启动服务器。

5运行tomcat测试
提供了自定义的实现ServletContainerInitializer接口的类,重写里的onStartup()方法,并且还提供了自定义的实现了WebApplicationInitializer接口的类,同样重写了onStartup()方法


可以看到如下的运行结果,我们自定义的,包括spring提供的,都执行了onStartup()方法
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方法会去调用创建跟容器的方法
1.4.2createRootApplicationContext创建跟容器的方法
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {SpringApplicationBuilder builder = createSpringApplicationBuilder();StandardServletEnvironment environment = new StandardServletEnvironment();// 首先初始化环境信息,并且servletContext已经创建好environment.initPropertySources(servletContext, null);builder.environment(environment);builder.main(getClass());ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);if (parent != null) {this.logger.info("Root context already created (using as parent).");servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);builder.initializers(new ParentContextApplicationContextInitializer(parent));}builder.initializers(new ServletContextApplicationContextInitializer(servletContext));builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);builder = configure(builder);// 构建了一个SpringApplicationSpringApplication application = builder.build();if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(getClass(), Configuration.class) != null) {application.addPrimarySources(Collections.singleton(getClass()));}Assert.state(!application.getAllSources().isEmpty(),"No SpringApplication sources have been defined. Either override the "+ "configure method or add an @Configuration annotation");// Ensure error pages are registeredif (this.registerErrorPageFilter) {application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));}// 调用这个方法return run(application);}
1.4.3run(SpringApplication application)

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