在這個啟動過程中會有各種SpringBoot對外提供的擴展接口來對不同啟動階段進行自定義操作。
了解啟動過程也是為了讓我們更好理解SpringBoot提供的擴展接口使用
tomcat會查詢context上下文中實現ServletContainerInitializer接口的類,然后調用類的onStartup(Set<Class<?>> c, ServletContext ctx)方法
Spring的SpringServletContainerInitializer實現了這個ServletContainerInitializer接口,
會獲取WebApplicationInitializer接口的實現類,調用onStartup()方法
SpringBoot的類SpringBootServletInitializer實現了Spring的WebApplicationInitializer擴展接口,
會在onStartup()方法中創建SpringApplication類,並調用SpringApplication.run()來完成啟動項目
與我們在開發時調用Application.main()方法啟動時一樣的原理
@SpringBootApplication public class Application extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { this.setRegisterErrorPageFilter(false); // 錯誤頁面有容器來處理,而不是SpringBoot return builder.sources(Application.class); } }
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
2 、觸發ApplicationStartingEvent事件
SpringBoot對Spring的ApplicationListener進行了一個包裝
在啟動過程中使用SpringApplicationRunListener接口的實現類EventPublishingRunListener來觸發各種事件
public interface SpringApplicationRunListener { starting ==> ApplicationStartingEvent environmentPrepared ==> ApplicationEnvironmentPreparedEvent contextPrepared ==> 無 contextLoaded ==》 ApplicationPreparedEvent started ==》 ApplicationStartedEvent running ==》 ApplicationReadyEvent failed ==》 ApplicationFailedEvent }
我們可以通過自定義SpringApplicationRunListener來在不同階段操作,也可以通過配置ApplicationListener來完成不同階段的操作。
這個開始啟動事件觸發操作:主要是進行日志系統初始化,字符集設置等操作
最開始觸發的監聽器:LoggingApplicationListener和BackgroundPreinitializer
LoggingApplicationListener日志系統實例生成,執行初始化前方法loggingSystem.beforeInitialize() BackgroundPreinitializer 后台線程預初始化任務,用於耗時任務的初始化 1.默認類型轉換器,format格式解析初始化 2.初始化校驗器 3. 默認處理表單數據的消息轉換器初始化的AllEncompassingFormHttpMessageConverter 4.初始化tomcat的MBeanFactory 5. 字符集初始化
3、獲取命令行參數和准備上下文環境
prepareEnvironment(listeners, applicationArguments)
主要是ConfigFileApplicationListener監聽這個事件
會調用EnvironmentPostProcessor 環境后置處理器接口
還有這個類本身后置處理器會將配置文件加載到environment環境的屬性源列表中
環境准備事件觸發操作:配置文件屬性源加載,日志系統配置環境屬性設置
EnvironmentPostProcessor 這個接口擴展處理上下文環境操作
DelegatingApplicationListener,這個委托代理監聽器只能代理ApplicationEnvironmentPreparedEvent環境准備完成事件
4、初始化上下文信息
為context上下文設置reader和scanner,用於獲取bean定義 調用ApplicationContextInitializer接口,對上下文進行初始化 將ApplicationArguments和SpringApplication作為bean注冊到BeanFactory中 load(context, sources.toArray(new Object[0])); 這里是將應用啟動類,即Application.java類,將其生成BeanDefinition即bean定義,綁定到beanFactory中 觸發ApplicationPreparedEvent事件
ApplicationContextInitializer接口, 對上下文進行初始化操作
觸發ApplicationPreparedEvent事件
網上資料:ApplicationPreparedEvent事件:在Bean定義加載之后、bean初始化之前,刷新上下文之前觸發
自己調試:這個bean定義加載只是對Application.java啟動類和內置的bean定義進行加載,我們項目中自定義的bean定義還是沒有進行加載。
將ConfigFileApplicationListener # PropertySourceOrderingPostProcessor添加到ApplicationContext的BeanFactoryPostProcessors列表中,這個是BeanFactory的后置處理器
將Bean名稱為:“springBootLoggingSystem”日志系統注冊到beanfactory中
配置beanfactory
設置bean類加載器,設置SpEL表達式解析器,注冊配置屬性編輯器,
初始化BeanPostProcessor(如ApplicationContextAwareProcessor),
設置忽略自動裝配的接口,
注冊默認環境bean(environment,systemProperties,systemEnvironment)
設置忽略自動裝配的接口是Spring默認指定的Aware及其子接口
當類A中有屬性是類B時,在初始化A的bean時,
如果類B是實現了Aware或其子接口時。不會為A自動注入類B
而是要通過B實現了Aware其子接口的接口方法來為A設置屬性
一般來說:實現了Aware其子接口的實現類都是作為工具類,或者配置類
BeanPostProcessor接口:對bean初始化前后做一些操作邏輯,比如設置bean的屬性,或對特殊的bean的方法進行調用等
5.3 postProcessBeanFactory(beanFactory)
context的子類在配置好BeanFactory后的擴展方法,
這里是AnnotationConfigServletWebServerApplicationContext類 添加了一個后置處理器,一個忽略自動注入接口
beanFactory.addBeanPostProcessor(WebApplicationContextServletContextAwareProcessor) beanFactory.ignoreDependencyInterface(ServletContextAware.class)
5.4 invokeBeanFactoryPostProcessors(beanFactory)
會將ApplicationContext中設置的beanFactoryPostProcessors列表
轉換成BeanDefinitionRegistryPostProcessor(registryProcessors 注冊處理器)
和BeanFactoryPostProcessor(regularPostProcessors 規則處理器)
內置beanFactory后置處理器列表
SharedMetadataReaderFactoryContextInitializer # CachingMetadataReaderFactoryPostProcessor
ConfigurationWarningsApplicationContextInitializer # ConfigurationWarningsPostProcessor
ConfigFileApplicationListener # PropertySourceOrderingPostProcessor
會先調用內置的注冊處理器的postProcessBeanDefinitionRegistry(registry);方法
進行自定義bean的注冊,或者對beanFactory中的bean定義的屬性值進行修改
比如
SharedMetadataReaderFactoryContextInitializer # CachingMetadataReaderFactoryPostProcessor
會將SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean類創建bean定義注冊到BeanFactory中,
還有修改ConfigurationAnnotationProcessor類的metadataReaderFactory屬性值為SharedMetadataReaderFactoryBean
也就是說只要有@Configuration、@Component、@ComponentScan、@Import、@ImportResource和@Bean中的其中一個注解,就會將bean注冊到beanfactory中
@Import注解處理,如果導入的是ImportSelector接口實現類,會調用接口方法String[] selectImports()
通過返回字符串數組。里面是配置類名稱。動態選擇要導入的配置類全名
除了解析上面注解定義的bean外
還會處理@PropertySource注解,將指定的配置屬性源設置到environment環境中
this.reader.loadBeanDefinitions(configClasses);這里會進行額外的bean定義加載:(被Import導入的類加載,方法定義的bean加載,資源文件中定義的bean)
后置處理器處理過程中會被調用三次
1. 第一次是執行ConfigurationClassPostProcessor后置處理器,獲取項目中的bean定義
執行過了后置處理器不會再次執行
2. 第二次是在所有bean定義中獲取有加@Order注解的BeanDefinitionRegistryPostProcessor實現類,執行后置處理器。 3. 第三次是再次獲取沒有執行過的處理器進行再次執行 比如mybatis的MapperScannerConfigurer就是在這里執行實現mybatis注解的bean定義加載 4. 最后是執行registryProcessors注冊器和regularPostProcessors規則處理器的postProcessBeanFactory(beanFactory)方法
PriorityOrdered接口
PropertySourcesPlaceholderConfigurer
Ordered接口
沒有實現上面接口的
org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata
preserveErrorControllerTargetClassPostProcessor
dataSourceInitializerSchedulerDependencyPostProcessor
5.5、registerBeanPostProcessors(beanFactory);
beanFactory.addBeanPostProcessor(postProcessor)
5.6、initMessageSource():一般是我們用來初始化我們國際化文件的
5.8、onRefresh():初始化其他的子容器類中的bean,同時創建spring的內置tomcat,
5.9、registerListeners():
添加用戶設置applicationListeners,然后從beanFactory獲取ApplicationListener, 然后發布需要earlyApplicationEvents事件 是從beanFactory中獲取所有實現ApplicationListener接口的類, getBeanNamesForType(ApplicationListener.class, true, false) 第一個type參數表示類型,第二個參數是否包含非單例的bean 第三個參數是否允許提前初始化, true表示允許。會導致bean提前初始化, false,表示不允許提前初始化,如果實在spring啟動過程中,這個參數需要設置為false
6.0、finishBeanFactoryInitialization(beanFactory);
緩存所有bean定義元數據 bean definition metadata
beanFactory.preInstantiateSingletons();
實例化所有剩余的(非延遲初始化)單例。
在這個過程中會調用beanPostProcessors,bean實例化前后的處理器
在這個方法里會處理實現Aware接口的Bean類
invokeAwareMethods(beanName, bean);方法會處理實現了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware接口實現了類的接口方法
接着調用applyBeanPostProcessorsBeforeInitialization 處理BeanPostProcessor bean調用初始化之前的處理
接着調用bean的初始化方法invokeInitMethods(beanName, wrappedBean, mbd);
接着調用bean的初始化方法之后的后置處理器
ApplicationContextAwareProcessor:
處理實現EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware這些接口的實現bean類在
在初始化回調之前調用對應接口方法做對應的操作。
比如initializingBean的afterPropertiesSet()方法之前或@Bean注解設置的init-method之前調用
WebApplicationContextServletContextAwareProcessor
處理實現了ServletContextAware接口的bean,可以通過setServletContext(ServletContext)方法獲取servletContext
完成上下文刷新,
清除上下文級別的resources緩存(MetadataReader封裝的資源,比如掃描的配置類)
初始化lifecycleProcessor,用來管理spring生命周期的onRefresh作用是容器啟動成功,onClose是只應用要關閉的時候
然后觸發ContextRefreshedEvent事件
refreshContext(context);執行完成
7、afterRefresh(context, applicationArguments);
這里沒有操作
8、觸發ApplicationStartedEvent事件
9、callRunners(context, applicationArguments)
從beanfactory中獲取ApplicationRunner和CommandLineRunner接口實現類
調用他們的run方法
10、觸發ApplicationReadyEvent事件