优质博文:IT-BLOG-CN
一、Spring Boot应用启动
一个Spring Boot应用的启动通常如下:
1@SpringBootApplication 2@Slf4j 3public class ApplicationMain { 4 public static void main(String[] args) { 5 ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args); 6 } 7} 8
执行如上代码,Spring Boot程序启动成功。事实上启动Spring Boot应用离不开SpringApplication。
所以,我们跟随SpringApplication的脚步,开始从源码角度分析Spring Boot的初始化过程。
btw,可参看例子一节,我对Spring Boot启动的拓展点都做了demo,可参照下面源码分析进行理解。
文档有一句话说了SpringApplication做了什么(目的):
1Create an appropriate ApplicationContext instance (depending on your classpath) Register a CommandLinePropertySource to expose command line arguments as Spring properties Refresh the application context, loading all singleton beans Trigger any CommandLineRunner beans 2
二、SpringApplication构造函数
启动代码先创建SpringApplication示例,在执行run方法:
1public static ConfigurableApplicationContext run(Class<?>[] primarySources, 2 String[] args) { 3 return new SpringApplication(primarySources).run(args); 4} 5
如下是SpringApplication的构造函数代码分析。
1this.resourceLoader = resourceLoader; 2Assert.notNull(primarySources, "PrimarySources must not be null"); 3this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 4//通过Classloader探测不同web应用核心类是否存在,进而设置web应用类型 5this.webApplicationType = WebApplicationType.deduceFromClasspath(); 6//找出所有spring.factories中声明的ApplicationContextInitializer并设置, 7//ApplicationContextInitializer定义了回调接口,在refresh()前初始化调用(即在prepareContext的applyInitializers方法中调用) 8setInitializers((Collection) getSpringFactoriesInstances( 9ApplicationContextInitializer.class)); 10//找出所有spring.factories中声明的ApplicationListener(细节往后再叙),ApplicationListener继承了 11//java.util.EventListener,实现了类似观察者模式的形式,通过实现ApplicationListener、SmartApplicationListener,能够监听Spring上下文的refresh、Prepared等事件或者是自定义事件 12setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 13//找出主启动类(有趣的是,是通过new一个runtime异常然后在异常栈里面找出来的) 14this.mainApplicationClass = deduceMainApplicationClass(); 15
在构造期间,主要做了:
1、判定应用类型,为后面创建不同类型的spring context做准备。
2、初始化ApplicationContextInitializer和ApplicationListener。
3、找出启动类。
三、run()源码解析
介绍run()方法前,先说说贯穿run方法的ApplicationRunListener,它有助于理解整个run()的运行周期。
写在这里:Spring Application事件机制
run()方法分析如下:
1//java.awt.headless,是J2SE的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。 2configureHeadlessProperty(); 3//将spring.factories中的SpringApplicationRunListener接口实现类拖出来,塞到SpringApplicationRunListeners(一个集合)中,统一批量执行 4SpringApplicationRunListeners listeners = getRunListeners(args); 5//触发runlistener的starting 6listeners.starting(); 7try { 8 ApplicationArguments applicationArguments = new DefaultApplicationArguments( 9 args); 10 ConfigurableEnvironment environment = prepareEnvironment(listeners, 11 applicationArguments); 12 //spring.beaninfo.ignore如果没有设置值,则把它设为true,具体情况具体设置, 13 //如果没用的话,把它设为true可以ignore掉classloader对于不存在的BeanInfo的扫描,提高性能。 14 configureIgnoreBeanInfo(environment); 15 //banner打印。自定义banner挺好玩的 16 Banner printedBanner = printBanner(environment); 17 //根据webApplicationType(一开始推断的应用类型)去新建applicationContext 18 context = createApplicationContext(); 19 //获取SpringBootExceptionReporter,回调接口类,提供启动时的异常报告 20 exceptionReporters = getSpringFactoriesInstances( 21 SpringBootExceptionReporter.class, 22 new Class[] { ConfigurableApplicationContext.class }, context); 23 //下面会说 24 prepareContext(context, environment, listeners, applicationArguments, 25 printedBanner); 26 refreshContext(context); 27 //do nothing 28 afterRefresh(context, applicationArguments); 29 //计时停止 30 stopWatch.stop(); 31 //打日志 32 if (this.logStartupInfo) { 33 new StartupInfoLogger(this.mainApplicationClass) 34 .logStarted(getApplicationLog(), stopWatch); 35 } 36 //启动 37 listeners.started(context); 38 //找出context的ApplicationRunner和CommandLineRunner,用AnnotationAwareOrderComparator排序,并执行 39 callRunners(context, applicationArguments); 40
下面再分别说说两个方法(prepareEnvironment、refreshContext)的代码。
四、prepareEnvironment
1private ConfigurableEnvironment prepareEnvironment( 2 SpringApplicationRunListeners listeners, 3 ApplicationArguments applicationArguments) { 4 // Create and configure the environment 5 ConfigurableEnvironment environment = getOrCreateEnvironment(); 6 configureEnvironment(environment, applicationArguments.getSourceArgs()); 7 //发布environment prepared事件 8 listeners.environmentPrepared(environment); 9 //将获取到的environment中的spring.main配置绑定到SpringApplication中, 10 //使用的是Binder这个spring boot2.0开始有的类 11 bindToSpringApplication(environment); 12 if (!this.isCustomEnvironment) { 13 environment = new EnvironmentConverter(getClassLoader()) 14 .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); 15 } 16 //附加的解析器将动态跟踪底层 Environment 属性源的任何添加或删除, 17 //关于ConfigurationPropertySourcesPropertySource和MutablePropertiySource 18 //将在Environment中作进一步讲解 19 ConfigurationPropertySources.attach(environment); 20 return environment; 21} 22
五、prepareContext
1private void prepareContext(ConfigurableApplicationContext context, 2 ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, 3 ApplicationArguments applicationArguments, Banner printedBanner) { 4 //为上下文设置environment(配置、profile) 5 context.setEnvironment(environment); 6 //对application做一些处理,设置一些组件, 7 //比如BeanNameGenerator,ApplicationConversionService(包含一些默认的Converter和formatter) 8 postProcessApplicationContext(context); 9 // 加载并运行ApplicationContextInitializer 10 applyInitializers(context); 11 listeners.contextPrepared(context); 12 if (this.logStartupInfo) { 13 logStartupInfo(context.getParent() == null); 14 logStartupProfileInfo(context); 15 } 16 // Add boot specific singleton beans 17 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 18 beanFactory.registerSingleton("springApplicationArguments", applicationArguments); 19 if (printedBanner != null) { 20 beanFactory.registerSingleton("springBootBanner", printedBanner); 21 } 22 if (beanFactory instanceof DefaultListableBeanFactory) { 23 ((DefaultListableBeanFactory) beanFactory) 24 .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); 25 } 26 // Load the sources 27 Set<Object> sources = getAllSources(); 28 Assert.notEmpty(sources, "Sources must not be empty"); 29 //Load beans(其实是由sources构建beanDefinition) into the application context. 30 //构建BeanDefinitionLoader并执行BeanDefinitionLoader.load() 31 load(context, sources.toArray(new Object[0])); 32 //执行contextLoaded事件 33 listeners.contextLoaded(context); 34} 35
六、容器refresh(refreshContext)
1private void refreshContext(ConfigurableApplicationContext context) { 2 refresh(context); 3 if (this.registerShutdownHook) { 4 try { 5 context.registerShutdownHook(); 6 } 7 catch (AccessControlException ex) { 8 // Not allowed in some environments. 9 } 10 } 11} 12
refreshContext会做两件事,
1、应用上下文刷新
2、注册shutdown钩子
我们来看看ServletWebServer的刷新。
1// ServletWebServerApplicationContext 2public final void refresh() throws BeansException, IllegalStateException { 3 try { 4 super.refresh(); 5 } 6 catch (RuntimeException ex) { 7 //停止webserver 8 stopAndReleaseWebServer(); 9 throw ex; 10 } 11} 12 13 14org.springframework.context.support.AbstractApplicationContext refresh() 15public void refresh() throws BeansException, IllegalStateException { 16 // 单线程执行 17 synchronized (this.startupShutdownMonitor) { 18 // Prepare this context for refreshing. 19 // 1、设置Spring容器的启动时间,撤销关闭状态,开启活跃状态。2、初始化属性源信息(Property)3、验证环境信息里一些必须存在的属性 20 prepareRefresh(); 21 // Tell the subclass to refresh the internal bean factory. 22 // 如果是RefreshtableApplicationContext会做了很多事情: 23 // 1、让子类刷新内部beanFactory ,创建IoC容器(DefaultListableBeanFactory--ConfigurableListableBeanFactory 的实现类) 24 // 2、加载解析XML文件(最终存储到Document对象中) 25 // 3、读取Document对象,并完成BeanDefinition的加载和注册工作 26 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 27 // Prepare the bean factory for use in this context. 28 //从Spring容器获取BeanFactory(Spring Bean容器)并进行相关的设置为后续的使用做准备: 29 //1、设置classloader(用于加载bean),设置表达式解析器(解析bean定义中的一些表达式),添加属性编辑注册器(注册属性编辑器) 30 //2、添加ApplicationContextAwareProcessor这个BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入。因为ApplicationContextAwareProcessor把这5个接口的实现工作做了 31 //3、设置特殊的类型对应的bean。BeanFactory对应刚刚获取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器 32 //4、注入一些其它信息的bean,比如environment、systemProperties等 33 prepareBeanFactory(beanFactory); 34 try { 35 // Allows post-processing of the bean factory in context subclasses. 36 postProcessBeanFactory(beanFactory); 37 // Invoke factory processors registered as beans in the context. 执行beanfactoryPostProcessor 38 invokeBeanFactoryPostProcessors(beanFactory); 39 // Register bean processors that intercept bean creation. 40 // 注册beanPostProcessor 41 registerBeanPostProcessors(beanFactory); 42 // Initialize message source for this context. 初始化messageSource 43 initMessageSource(); 44 // Initialize event multicaster for this context. 45 initApplicationEventMulticaster(); 46 // Initialize other special beans in specific context subclasses. 47 onRefresh(); 48 // Check for listener beans and register them. 49 registerListeners(); 50 // Instantiate all remaining (non-lazy-init) singletons. 51 finishBeanFactoryInitialization(beanFactory); 52 // Last step: publish corresponding event. 53 finishRefresh(); 54 } catch (BeansException ex) { 55 if (logger.isWarnEnabled()) { 56 logger.warn("Exception encountered during context initialization - " + 57 "cancelling refresh attempt: " + ex); 58 } 59 // Destroy already created singletons to avoid dangling resources. 60 destroyBeans(); 61 // Reset 'active' flag. 62 cancelRefresh(ex); 63 // Propagate exception to caller. 64 throw ex; 65 } 66 67 finally { 68 // Reset common introspection caches in Spring's core, since we 69 // might not ever need metadata for singleton beans anymore... 70 resetCommonCaches(); 71 } 72 } 73} 74
七、postProcessBeanFactory()
设置BeanFactory之后再进行后续的一些BeanFactory操作。
不同的Context会进行不同的操作。 比如,AnnotationConfigServletWebServerApplicationContext
1protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { 2 // 父类实现,会注册web应用特有的factory scope, 3 super.postProcessBeanFactory(beanFactory); 4 //查看basePackages属性,如果设置了会使用ClassPathBeanDefinitionScanner去扫描basePackages包下的bean并注 5 if (this.basePackages != null && this.basePackages.length > 0) { 6 this.scanner.scan(this.basePackages); 7 } 8 // 查看annotatedClasses属性,如果设置了会使用AnnotatedBeanDefinitionReader去注册这些bean 9 if (!this.annotatedClasses.isEmpty()) { 10 this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); 11 } 12} 13
八、invokeBeanFactoryPostProcessors()
1/** 2 * Instantiate and invoke all registered BeanFactoryPostProcessor beans, 3 * respecting explicit order if given. 4 * <p>Must be called before singleton instantiation. 5 */ 6protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { 7 //执行AbstractContext持有的BeanFactory后置处理器 8 //这些处理器是之前ContextInitializer 9 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); 10 11 // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime 12 // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) 13 // 如果通过-javaagent参数设置了LTW的织入器类包,那么增加LTW的BeanProcessor。 14 if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { 15 beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); 16 beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); 17 } 18} 19
从容器中找出BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor(二者的区别是,一个使用BeanDefinitionRegistry作处理,一个使用BeanFactory做处理), 并按一定的规则顺序执行。
ConfigurationClassPostProcessor的优先级为最高,它会对项目中的@Configuration注解修饰的类(@Component、@ComponentScan、@Import、@ImportResource修饰的类也会被处理)进行解析,解析完成之后把这些bean注册到BeanFactory中。 需要注意的是这个时候注册进来的bean还没有实例化。
ConfigurationClassPostProcessor的流程之后会独立进行分析。
九、registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)方法
···java
/**
- Instantiate and invoke all registered BeanPostProcessor beans,
- respecting explicit order if given.
- Must be called before any instantiation of application beans.
*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//委派PostProcessorRegistrationDelegate去做
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
1从Spring容器中按一定顺序(PriorityOrdered、Ordered、非PriorityOrdered非Ordered)找出实现了BeanPostProcessor接口的bean,并设置到BeanFactory的属性中。之后bean被实例化的时候会调用这个BeanPostProcessor。 2 3## 十、initMessageSource() 4初始化一些国际化相关的属性。 5 6Spring boot的国际化配置可阅读MessageSourceAutoConfiguration。 默认情况会设置一个DelegatingMessageSource,是一个空实现,因为ApplicationContext接口拓展了MessageSource接口,所以Spring容器都有getMessage方法, 可是,在实现上又允许空MessageSource,所以,通过一个DelegatingMessageSource去适配。 7 8## 十一、initApplicationEventMulticaster() 9Initialize event multicaster for this context. 初始化事件广播器。默认实现是SimpleApplicationEventMulticaster。 10 11onRefresh() 12模板方法,给不同的Spring应用容器去实例化一些特殊的类。 13 14比如,AnnotationConfigServletWebServerApplicationContext、AnnotationConfigReactiveWebServerApplicationContext会去创建web server(createWebServer())。 spring boot的mvc内置支持有tomcat、Undertow、jetty三种server,而reactive web server则内置支持tomcat、jetty、netty三种。 15java 16// Unlike Jetty, all Tomcat threads are daemon threads. We create a 17// blocking non-daemon to stop immediate shutdown 18startDaemonAwaitThread(); 19
btw,如果是tomcat server的话,spring boot会启动多一个线程防止退出。
十二、registerListeners()
把BeanFactory的ApplicationListener拿出来塞到事件广播器里。
如果ApplicationContext的earlyApplicationEvents属性有值,则广播该属性持有的early事件。
十三、finishBeanFactoryInitialization(beanFactory)
实例化BeanFactory中已经被注册但是未实例化的所有实例(懒加载的不需要实例化)。
比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。
十四、finishRefresh()
1// ReactiveWebServerApplicationContext 2 @Override 3 protected void finishRefresh() { 4 super.finishRefresh(); 5 WebServer webServer = startReactiveWebServer(); 6 if (webServer != null) { 7 publishEvent(new ReactiveWebServerInitializedEvent(webServer, this)); 8 } 9 } 10 11// AbstractApplicationContext 12 /** 13 * Finish the refresh of this context, invoking the LifecycleProcessor's 14 * onRefresh() method and publishing the 15 * {@link org.springframework.context.event.ContextRefreshedEvent}. 16 */ 17 protected void finishRefresh() { 18 // Clear context-level resource caches (such as ASM metadata from scanning). 19 // 容器完成刷新,清除资源缓存 20 clearResourceCaches(); 21 22 // Initialize lifecycle processor for this context. 23 // 初始化lifeCycleProcessor, 默认实现是DefaultLifeCycleProcessor,实现了BeanFactoryAware接口,通过BeanFactory找出LifeCycle bean 24 // 可通过自定义实现LifeCycle接口的Bean,来监听容器的生命周期。 25 initLifecycleProcessor(); 26 27 // Propagate refresh to lifecycle processor first. 28 //粗发生命周期处理器的onRefresh方法,顺带一说,在程序正常退出时,会粗发shutdownHook,那时会粗发生命周期处理器的onClose方法 29 getLifecycleProcessor().onRefresh(); 30 31 // Publish the final event. 32 // 广播ContextRefreshed事件 33 publishEvent(new ContextRefreshedEvent(this)); 34 35 // Participate in LiveBeansView MBean, if active. 36 // 将ApplicationContext注册到Spring tool suite里 37 LiveBeansView.registerApplicationContext(this); 38 } 39
十五、resetCommonCaches()
1// Reset common introspection caches in Spring's core, since we 2// might not ever need metadata for singleton beans anymore... 3resetCommonCaches(); 4
最后会在finally执行resetCommonCaches(),清除一些Spring core、beans加载和解析的Bean信息缓存(因为对于singleton bean来说已经不需要了)。
十六、流程整理
最后,按照启动阶段整理一幅全景图。

十七、例子
在github里,我把Spring Boot应用启动的拓展组件(自定义的应用初始器、监听器、事件、ApplicationRunner)都写了例子,可参照阅读。 代码在这 | spring-boot-none-startup
日志如下:
12020-05-20 18:30:11.625 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : environmentPrepared, env:StandardEnvironment {activeProfiles=[dev], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application-dev.yml]'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}]} 2 3 . ____ _ __ _ _ 4 /\ / ___'_ __ _ _(_)_ __ __ _ 5
( ( )___ | '_ | '| | ’ / ` |
/ )| |)| | | | | || (| | ) ) ) )
’ | | .__|| ||| |_ , | / / / /
=========||==============| /=///_/
:: Spring Boot :: (v2.1.3.RELEASE)
12020-05-20 18:30:11.832 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : contextPrepared, ctx:org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970 22020-05-20 18:30:11.838 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : Starting ApplicationMain on DESKTOP-OLDGHC1 with PID 81568 ( started by teash in ) 32020-05-20 18:30:11.838 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : The following profiles are active: dev 42020-05-20 18:30:11.894 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : contextLoaded, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970 52020-05-20 18:30:12.404 INFO 81568 --- [ main] .s.b.s.n.s.SimpleApplicationContextAware : SimpleApplicationContextAware and send SimpleAppEvent 62020-05-20 18:30:12.441 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.e.SimpleEventListener : event: net.teaho.demo.spring.boot.startup.none.spring.event.SimpleAppEvent[source=event source], source: event source 72020-05-20 18:30:12.444 INFO 81568 --- [ main] n.t.d.s.b.s.n.config.BeanConfiguration : [net.teaho.demo.spring.boot.startup.none.spring.spi.DemoSpringLoaderImpl@c96a4ea] 82020-05-20 18:30:12.484 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.l.LoggingLifeCycle : In Life cycle bean start(). 92020-05-20 18:30:12.496 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : Started ApplicationMain in 1.573 seconds (JVM running for 3.195) 102020-05-20 18:30:12.496 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : started, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020 112020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.EchoApplicationRunner : EchoApplicationRunner running, args:org.springframework.boot.DefaultApplicationArguments@45673f68 122020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.EchoCommandLineRunner : EchoCommandLineRunner running 132020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : running, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020 142020-05-20 18:30:12.500 INFO 81568 --- [ Thread-3] n.t.d.s.b.s.n.s.l.LoggingLifeCycle : In Life cycle bean stop(). 15
《Spring boot启动原理及相关组件》 是转载文章,点击查看原文。
