Spring Boot学习笔记03--深入了解SpringBoot的启动过程

https://www.jianshu.com/p/cb5cb5937686

摘要

看完本文你将掌握如下知识点:

SpringApplication的作用及运行过程

SpringBootServletInitializer的作用及运行过程

PS:本节内容略显枯燥,如果对SpringBoot的启动过程不感兴趣,可以略过。

SpringBoot系列:Spring Boot学习笔记

深入了解SpringApplication

@SpringBootApplicationpublicclassSpringBootWebDemoApplication{publicstaticvoidmain(String[] args){        SpringApplication.run(SpringBootWebDemoApplication.class, args);    }}

这就是SpringBoot的启动入口,通过前面的学习我们大体上了解了@SpringBootApplication的作用,接下来我们来认识一下SpringApplication

SpringApplication (Spring Boot Docs 1.4.2.RELEASE API)。

SpringApplication.run(SpringBootWebDemoApplication.class, args);

通过源码我们来看一下SpringApplication.run()方法的执行过程

1.调用static方法

//1publicstaticConfigurableApplicationContextrun(Object source, String... args){returnrun(newObject[]{source}, args);}publicstaticConfigurableApplicationContextrun(Object[] sources, String[] args){return(newSpringApplication(sources)).run(args);}

2.创建SpringApplication对象

//2publicSpringApplication(Object... sources){this.bannerMode = Mode.CONSOLE;//banner的打印模式,此时是控制台模式this.logStartupInfo =true;//开启日志this.addCommandLineProperties =true;//启用CommandLinePropertiesthis.headless =true;//开启headless模式支持this.registerShutdownHook =true;//启用注册ShutdownHook,用于在非Web应用中关闭IoC容器和资源this.additionalProfiles =newHashSet();this.initialize(sources);//初始化}

PS:Headless参考资料:在 Java SE 平台上使用 Headless 模式

3.初始化相关对象和属性

//3privatevoidinitialize(Object[] sources){if(sources !=null&& sources.length >0) {this.sources.addAll(Arrays.asList(sources));        }//3.1判断是否是web运行环境,如果classpath中是否含有**WEB_ENVIRONMENT_CLASSES**指定的全部类,则返回truethis.webEnvironment =this.deduceWebEnvironment();//3.2找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));//3.3找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));//3.4获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例this.mainApplicationClass =this.deduceMainApplicationClass();    }

3.1 判断是否是web运行环境

如果classpath中是否含有WEB_ENVIRONMENT_CLASSES指定的全部类,则返回true,用于创建指定类型的ApplicationContext对象。

//3.1privatestaticfinalString[] WEB_ENVIRONMENT_CLASSES =newString[]{"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"};

3.2 大体的过程就是通过SpringFactoriesLoader检索META-INF/spring.factories,找到声明的所有ApplicationContextInitializer的实现类并将其实例化。

ApplicationContextInitializer是Spring框架中的接口,其作用可以理解为在ApplicationContext执行refresh之前,调用ApplicationContextInitializer的initialize()方法,对ApplicationContext做进一步的设置和处理。

publicinterfaceApplicationContextInitializer{voidinitialize(C var1);}

spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer

# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer

spring-boot-autoconfigure-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer

# Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

3.3 大体的过程就是通过SpringFactoriesLoader检索META-INF/spring.factories,找到声明的所有ApplicationListener的实现类并将其实例化。

ApplicationListener是Spring框架中的接口,就是事件监听器,其作用可以理解为在SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。

publicinterfaceApplicationListenerextendsEventListener{voidonApplicationEvent(E var1);}

SpringBoot只提供了一个SpringApplicationRunListener的实现类,就是EventPublishingRunListener,起作用就是在SpringBoot启动过程中,负责注册ApplicationListener监听器,在不同的时点发布不同的事件类型,如果有哪些ApplicationListener的实现类监听了这些事件,则可以接收并处理。

publicinterfaceSpringApplicationRunListener{//通知监听器,SpringBoot开始执行voidstarted();//通知监听器,Environment准备完成voidenvironmentPrepared(ConfigurableEnvironment var1);//通知监听器,ApplicationContext已经创建并初始化完成voidcontextPrepared(ConfigurableApplicationContext var1);//通知监听器,ApplicationContext已经完成IoC配置加载voidcontextLoaded(ConfigurableApplicationContext var1);//通知监听器,SpringBoot启动完成voidfinished(ConfigurableApplicationContext var1, Throwable var2);}

spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationListener

# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.context.FileEncodingApplicationListener,\org.springframework.boot.context.config.AnsiOutputApplicationListener,\org.springframework.boot.context.config.ConfigFileApplicationListener,\org.springframework.boot.context.config.DelegatingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\org.springframework.boot.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.logging.LoggingApplicationListener

spring-boot-autoconfigure-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationListener

# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer

spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的SpringApplicationRunListener

# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener

3.4 获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例。

4.核心方法

//4publicConfigurableApplicationContextrun(String... args){//开启任务执行时间监听器StopWatch stopWatch =newStopWatch();        stopWatch.start();        ConfigurableApplicationContext context =null;        Object analyzers =null;//设置系统属性『java.awt.headless』,为true则启用headless模式支持this.configureHeadlessProperty();//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,//之后逐个调用其started()方法,广播SpringBoot要开始执行了。SpringApplicationRunListeners listeners =this.getRunListeners(args);        listeners.started();try{            DefaultApplicationArguments ex =newDefaultApplicationArguments(args);//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。ConfigurableEnvironment environment =this.prepareEnvironment(listeners, ex);//决定是否打印BannerBanner printedBanner =this.printBanner(environment);//根据webEnvironment的值来决定创建何种类型的ApplicationContext对象//如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext//否则创建org.springframework.context.annotation.AnnotationConfigApplicationContextcontext =this.createApplicationContext();//注册异常分析器newFailureAnalyzers(context);//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。this.prepareContext(context, environment, listeners, ex, printedBanner);//初始化所有自动配置类,调用ApplicationContext的refresh()方法this.refreshContext(context);//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。//该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。this.afterRefresh(context, ex);//调用所有的SpringApplicationRunListener的finished()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。listeners.finished(context, (Throwable)null);//关闭任务执行时间监听器stopWatch.stop();//如果开启日志,则答应执行是时间if(this.logStartupInfo) {                (newStartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);            }returncontext;        }catch(Throwable var9) {//调用异常分析器打印报告,调用所有的SpringApplicationRunListener的finished()方法将异常信息发布出去this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);thrownewIllegalStateException(var9);        }    }

spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的FailureAnalyzerFailureAnalysisReporters

# Failure Analyzersorg.springframework.boot.diagnostics.FailureAnalyzer=\org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer# FailureAnalysisReportersorg.springframework.boot.diagnostics.FailureAnalysisReporter=\org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

说明

SpringBoot的启动过程,实际上就是对ApplicationContext的初始化过程。

ApplicationContext创建后立刻为其设置Environmen,并由ApplicationContextInitializer对其进一步封装。

通过SpringApplicationRunListener在ApplicationContext初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。

初始化过程中完成IoC的注入,包括通过@EnableAutoConfiguration导入的各种自动配置类。

初始化完成前调用ApplicationRunner和CommandLineRunner的实现类。

扩展SpringApplication

通过上面的学习,我们基本上了解了,如果要对SpringApplication进行扩展,我们可以选择如下三种方案:

创建ApplicationContextInitializer的实现类

创建ApplicationListener的实现类

创建ApplicationRunner和CommandLineRunner的实现类

1.可以通过如下方式加载自定义的ApplicationContextInitializerApplicationListener

@SpringBootApplicationpublicclassSpringBootWebDemoApplication{publicstaticvoidmain(String[] args){//SpringApplication.run(SpringBootWebDemoApplication.class, args);SpringApplication springApplication =newSpringApplication(SpringBootWebDemoApplication.class);        springApplication.addInitializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2);        springApplication.addListeners(MyApplicationListener1,MyApplicationListener2);        springApplication.run(args);    }}

2.也可以在当前项目的类路径下创建META-INF/spring.factories文件,并声明相应的ApplicationContextInitializerApplicationListener

org.springframework.context.ApplicationContextInitializer=\xxx.xxx.MyApplicationContextInitializer1,\xxx.xxx.MyApplicationContextInitializer2# Application Listenersorg.springframework.context.ApplicationListener=\xxx.xxx.MyApplicationListener1,\xxx.xxx.MyApplicationListener2

3.至于ApplicationRunner和CommandLineRunner,只需要在其实现类上加上@Component注解或者在@Configuration配置类中通过@Bean注解注入。

深入了解SpringBootServletInitializer

熟悉了SpringApplication的原理之后,我们再来了解SpringBootServletInitializer的原理就比较容易了。

publicclassServletInitializerextendsSpringBootServletInitializer{@OverrideprotectedSpringApplicationBuilderconfigure(SpringApplicationBuilder application){returnapplication.sources(DemoWarApplication.class);    }}

SpringBootServletInitializer就是一个org.springframework.web.context.WebApplicationContext,容器启动时会调用其onStartup(ServletContext servletContext)方法,接下来我么就来看一下这个方法:

publicvoidonStartup(ServletContext servletContext)throwsServletException{this.logger = LogFactory.getLog(this.getClass());finalWebApplicationContext rootAppContext =this.createRootApplicationContext(servletContext);if(rootAppContext !=null) {            servletContext.addListener(newContextLoaderListener(rootAppContext) {publicvoidcontextInitialized(ServletContextEvent event){                }            });        }else{this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");        }    }

这里的核心方法就是createRootApplicationContext(servletContext):

protectedWebApplicationContextcreateRootApplicationContext(ServletContext servletContext){//创建SpringApplicationBuilder,并用其生产出SpringApplication对象SpringApplicationBuilder builder =this.createSpringApplicationBuilder();        builder.main(this.getClass());        ApplicationContext parent =this.getExistingRootWebApplicationContext(servletContext);if(parent !=null) {this.logger.info("Root context already created (using as parent).");            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);            builder.initializers(newApplicationContextInitializer[]{newParentContextApplicationContextInitializer(parent)});        }//初始化并封装SpringApplicationBuilder对象,为SpringApplication对象增加ApplicationContextInitializer和ApplicationListener做准备builder.initializers(newApplicationContextInitializer[]{newServletContextApplicationContextInitializer(servletContext)});        builder.listeners(newApplicationListener[]{newServletContextApplicationListener(servletContext)});//指定创建的ApplicationContext类型builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);//传递入口类,并构建SpringApplication对象//可以通过configure()方法对SpringBootServletInitializer进行扩展builder =this.configure(builder);        SpringApplication application = builder.build();if(application.getSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) !=null) {            application.getSources().add(this.getClass());        }        Assert.state(!application.getSources().isEmpty(),"No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");if(this.registerErrorPageFilter) {            application.getSources().add(ErrorPageFilter.class);        }//最后调用SpringApplication的run方法returnthis.run(application);    }

说明

SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,并最终调用SpringApplication的run方法的过程。

扩展SpringBootServletInitializer

与扩展SpringApplication类似,ApplicationContextInitializerApplicationListener可以基于SpringApplicationBuilder提供的public方法进行扩展

publicclassServletInitializerextendsSpringBootServletInitializer{@OverrideprotectedSpringApplicationBuilderconfigure(SpringApplicationBuilder application){        application.initializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2);        application.listeners(MyApplicationListener1,MyApplicationListener2)returnapplication.sources(DemoWarApplication.class);    }}

小礼物走一走,来简书关注我

作者:飘逸峰

链接:https://www.jianshu.com/p/cb5cb5937686

來源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

你可能感兴趣的:(spring,boot)