springboot启动流程
一句話總結:在context的refresh方法中,需要注冊bean definition,實例化bean.在加載bean defintion的時候使用ConfigurationClassParser類來解析我們的主類。然后在解析主類的時候發現了@EnableAutoConfiguratio注解中的@Import注解,就去處理@Import注解中的value值,然后就使用ImportSelector來獲取被配置在spring.factories中的類。這些類通常是AutoConfiguration。這些configuration中包含了各種各樣的bean
一切的開始源于AppBootstrap.main方法,它調用了SpringApplication.run(AppBootstrap.class)方法。
在SpringApplication.run(AppBootstrap.class)方法中
//SpringApplication.java public static ConfigurableApplicationContext run(Object[] sources, String[] args) {return (new SpringApplication(sources)).run(args); }所以我們可以看到spring啟動流程大概分為兩部,new和run。所以本篇文章會從分兩個部分說明springboot的啟動流程。在訴說啟動流程的過程中,會將spring boot 的自動配置有關的東西重點拎出來。因為自動配置,我們可以什么都不做就可以啟動一個web服務,不需要在web.xml中配置servlet,也不需要在applicationcontext.xml中配置componentScan。
new SpringApplication(sources)
public SpringApplication(Object... sources) {//設置打印模式this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = new HashSet();//主要設置listeners,initializer。listeners監聽env,context的事件。initializer是在prepareContext中用來初始化contextthis.initialize(sources);}屬性設置
headless 刑天模式?
Headless模式是在缺少顯示屏、鍵盤或者鼠標時的系統配置。?
在java.awt.toolkit和java.awt.graphicsenvironment類中有許多方法,除了對字體、圖形和打印的操作外還需要調用顯示器、鍵盤和鼠標的方法。在電腦缺少對應硬件設備的時候會拋出HeadlessException異常。但是有些類,如Canvas也可以在headless下正常使用。?
headless?
headless
初始化
initialize(sources)
private void initialize(Object[] sources) {if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}/*deduceWebEnvironment方法通過判斷javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext這兩個Class是否存在判斷是否是Web環境*/this.webEnvironment = this.deduceWebEnvironment();this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));//設置mainApplicationClass為AppBootstrapthis.mainApplicationClass = this.deduceMainApplicationClass();}ApplicationContextInitializer
在setInitializer方法中被實例化的initializer有:?
查看這些initializer的類圖:?
他們都實現了ApplicationContextInitializer。顧名思義,和上下文有關的初始化器。也就是說這些實例將在上下文初始化的時候prepareContext被使用。
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {void initialize(C var1); }ApplicationListener
在setListeners方法中被實例的話listener包含:?
這些listener都實現了ApplicationListener接口。這個接口表示這個接口將監聽對應的ApplicationEvent事件
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E var1); }整理幾個listener和其監聽的事件如下:
| ConfigFileApplicationListener | ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent |
| AnsiOutputApplicationListener | ApplicationEnvironmentPreparedEvent |
| LoggingApplicationListener | ApplicationStartingEvent,ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ContextClosedEvent,ApplicationFailedEvent |
| ClasspathLoggingApplicationListener | ApplicationEnvironmentPreparedEvent,ApplicationFailedEvent |
| BackgroundPreinitializer | ApplicationEnvironmentPreparedEvent |
| DelegatingApplicationListener | ApplicationEnvironmentPreparedEvent,** |
| ParentContextCloserApplicationListener | ParentContextAvailableEvent |
| ClearCachesApplicationListener | ContextRefreshedEvent |
| FileEncodingApplicationListener | ApplicationEnvironmentPreparedEvent |
| LiquibaseServiceLocatorApplicationListener | ApplicationStartingEvent |
整理這些對應關系的目的在于,在啟動流程中,會出發許多ApplicationEvent。這時會調用對應的listener的onApplicationEvent方法。?
這里是一種觀察者模式。對于觀察者模式,個人的理解為:被觀察者持有觀察者的引用,在發生某些事情的時候,調用觀察者的方法即可
setInitailizers和setListeners方法
在setInitailizers和setListeners方法中都用到了getSpringFactoriesInstances.代碼如下
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {return this.getSpringFactoriesInstances(type, new Class[0]);}private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}在其中使用SpringFactoriesLoader來根據type,classLoader從幾個jar包之類的spring.factories中獲取類名,之后實例化對應的類。代碼如下:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {String factoryClassName = factoryClass.getName();try {//會查找幾個Spring的jar包中的META-INF/spring.factories文件。Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");ArrayList result = new ArrayList();while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));String factoryClassNames = properties.getProperty(factoryClassName);result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));}return result;} catch (IOException var8) {throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);}}SpringFactoriesLoader會查找幾個Spring的jar包中的META-INF/spring.factories文件。被查找的factories文件的內容大致如下:
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer# Application Listeners org.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# Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor# Failure Analyzers org.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# FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter在本項目中,被查找的有:
- spring-boot
- spring-boot-autoconfigure
- spring-beans
- spring-boot-test
- spring-boot-test-autoconfigure
- spring-test?
這些jar包都是被直接或者間接的被項目所依賴.
spring.factories文件的作用也就間接的實現了自動化配置。我們可以在項目下創建自己的spring.factories文件。來向spring boot啟動流程中加入我們自己需要的東西。
現在回到SetListener和setInitializer方法。他們使用SpringFactoriesLoader從spring.factories文件中獲取對應type的類名。然后實例化這些類。并將這些實例保存在springApplication的對應屬性中。?
private List<ApplicationContextInitializer<?>> initializers;?
或者private List<ApplicationListener<?>> listeners;
由于listener和initializer的構造函數幾乎為null。所以不用去關心他們在構造函數中做了什么。
run
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();//context,上下文,我們的重點對象ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;this.configureHeadlessProperty();//new SpringApplicationRunListeners用它來統一管理listeners,在事件發生的時候調用他們SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();//如果有監聽器監聽啟動事件,則執行對應的動作try {//從命令行中讀取參數作為propertySource,放入到這里,會加入到env中ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//創建env,加載配置文件,系統的屬性,profile文件,application.yml, 初始化日志系統ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);//打印bannerBanner printedBanner = this.printBanner(environment);//創建contextcontext = this.createApplicationContext();new FailureAnalyzers(context);this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);this.refreshContext(context);this.afterRefresh(context, applicationArguments);listeners.finished(context, (Throwable)null);stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}return context;} catch (Throwable var9) {this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);throw new IllegalStateException(var9);}}獲取運行時監聽器監聽運行時發生的事件:getRunListeners
SpringApplicationRunListeners listeners = this.getRunListeners(args);首先看SpringApplicationRunListeners類。類中包含了一個SpringApplicationRunListener的集合.
class SpringApplicationRunListeners {private final Log log;private final List<SpringApplicationRunListener> listeners; } public interface SpringApplicationRunListener {void starting();void environmentPrepared(ConfigurableEnvironment var1);void contextPrepared(ConfigurableApplicationContext var1);void contextLoaded(ConfigurableApplicationContext var1);void finished(ConfigurableApplicationContext var1, Throwable var2); }也就是說?
在run方法中構造了一個SpringRunApplicationListeners,其中包含多個SpringRunApplicationListener,用來監聽應用啟動過程中的start,environmentPrepared,contextPrepared,contextLoaded,finished等事件。
SpringRunApplicationLinsteners中到底包含哪些RunListener呢?包含從spring.factories中實例化的。在這里,getSpringFactoroiesIntsances方法返回只包含一個實例的集合,這個實例是EventPublishingRunListener
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class[]{SpringApplication.class, String[].class};return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}下面看EventPublishingRunListener類。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {private final SpringApplication application;private final String[] args;private final SimpleApplicationEventMulticaster initialMulticaster;public EventPublishingRunListener(SpringApplication application, String[] args) {//在這里,application即SpringApplicationthis.application = application;this.args = args;this.initialMulticaster = new SimpleApplicationEventMulticaster();Iterator var3 = application.getListeners().iterator();//在SpringApplication.initailize()方法中初始化的listeners被加入到EventPublishingRunListener里面的initialMulticaster中while(var3.hasNext()) {ApplicationListener<?> listener = (ApplicationListener)var3.next();this.initialMulticaster.addApplicationListener(listener);}} }EventPublishingRunListener通過引用SimpleApplicationEventMulticaster來完成事件的發布和ApplicationListener的管理。
接下來我們仔細看SimpleApplicationEventMulticaster這個類。?
從SimpleApplicationEventMulticaster類的結構中可以看到multicaster使用ListenerRetriever來管理ApplicationListener。使用了retrieverCache:ConcurrentHashMap
廣播ApplicationStartedEvent事件:listeners.starting()
在上一步使用SpringRunApplicationListeners將所有的listener管理起來后,開始傳播start事件
//EventPublishingRunListenerpublic void starting() {this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));}需要注意的是:
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);//在這一步getApplicationListeners(event,type)中,會從所有的listener中取出支持這個事件類型的listener,一個新的retriever會持有這些listener的引用作為value,另外以(event,source)作為key,存在retrieverCache中。Iterator var4 = this.getApplicationListeners(event, type).iterator();while(var4.hasNext()) {final ApplicationListener<?> listener = (ApplicationListener)var4.next();Executor executor = this.getTaskExecutor();if (executor != null) {executor.execute(new Runnable() {public void run() {SimpleApplicationEventMulticaster.this.invokeListener(listener, event);}});} else {this.invokeListener(listener, event);}}}在listener.starting()這一部中,幾個listener都沒有做什么動作。
構造應用參數:new DefaultApplicationArguments(args)
創建DefaultApplicationArguments的作用是將參數存儲在DefaultApplicationArguments的內部類Source中。?
其中Source的類結構如圖:?
我們追尋Source的構造函數可以發現,使用SimpleCommandLineArgsParser來解析參數,將解析后的參數存到PropertySource的source屬性中。?
這里由于沒有什么參數,所以略過。
創建和準備環境:environment
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {//創建env,如果是web環境,則創建StandardServletEnvironment,如果不是StandardEnvironmentConfigurableEnvironment environment = this.getOrCreateEnvironment();//配置envthis.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());//廣播environment事件listeners.environmentPrepared((ConfigurableEnvironment)environment);if (!this.webEnvironment) {environment = (new EnvironmentConverter(this.getClassLoader())).convertToStandardEnvironmentIfNecessary((ConfigurableEnvironment)environment);}return (ConfigurableEnvironment)environment;}這段代碼主要分為幾個步驟:?
1. 創建Env?
2. 配置Env?
3. 調用監聽器廣播ApplicationEnvironmentPreparedEvent事件
創建Env
private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;} else {return (ConfigurableEnvironment)(this.webEnvironment ? new StandardServletEnvironment() : new StandardEnvironment());}}如果是Web環境,則創建StandardServletEnvironment,否則創建StandardEnvironment.?
下面的代碼是有關Environment和ConfigurableEnvironment接口.?
我們可以從接口中看出Environment主要作用為:獲取系統的環境和spring的profile文件中的內容,存儲到PropertySource中。
有關Env的類圖:?
在environment初始化之后,PropertySources中包含的幾個PropertySource為4個:?
1. servletContextInitParam?
2. servletConfigInitParams?
3. systemEnv?
4. systemProperties
簡單說下PropertySource的結構
//代表了name/value屬性對的資源。source可以任何一種封裝了屬性值得類型??梢允荘roperties,Map,ServletContext,ServletConfig //一般都很少單獨使用PropertySource,而是使用PropertySources public abstract class PropertySource<T> {protected final Log logger;protected final String name;protected final T source; } //PropertySources中聚合了多個PropertySource,包含PropertyResolver的實現可以實現從PropertySource中搜索 public interface PropertySources extends Iterable<PropertySource<?>> {boolean contains(String var1);PropertySource<?> get(String var1); }當和@Configuration一起工作時,@PropertySource注解可以方便得為當前環境加入配置文件?
和PropertySource相關的類圖:?
配置env
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {this.configurePropertySources(environment, args);this.configureProfiles(environment, args);}//這兩個函數分別用來添加命令行參數的PropertySource和設置activeProfile。由于項目中沒有這些則略過廣播ApplicationEnvironmentPreparedEvent事件
調用RunListener,廣播ApplicationEnvironmentPreparedEvent事件。受到影響的listener列在下表
| ConfigFileApplicationListener | ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent |
| AnsiOutputApplicationListener | ApplicationEnvironmentPreparedEvent |
| LoggingApplicationListener | ApplicationStartingEvent,ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ContextClosedEvent,ApplicationFailedEvent |
| ClasspathLoggingApplicationListener | ApplicationEnvironmentPreparedEvent,ApplicationFailedEvent |
| BackgroundPreinitializer | ApplicationEnvironmentPreparedEvent |
| DelegatingApplicationListener | ApplicationEnvironmentPreparedEvent,** |
| FileEncodingApplicationListener | ApplicationEnvironmentPreparedEvent |
加載配置文件:ConfigFileApplicationListener。注意application.proporties文件在這一步加載
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {//ConfigFileApplicationListener使用EnvironmentPostProcessor來處理environmentList<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);Iterator var3 = postProcessors.iterator();while(var3.hasNext()) {EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}}EnvironmentPostProcessor會在應用上下文刷新之前定制應用的環境。EnvironmentPostProcessor的實現類需要注冊在META-INF/spring.factories文件中,并且用類的全路徑名作為key。并且其實現類被鼓勵實現Ordered接口或者@Order注解,Ordered接口和@Order的作用在于將容器內的bean排序。?
EnvironmentPostProcessor從中所周知的路徑中加載屬性文件用來配置應用上下文的環境。默認情況是:從classpath:,file:./,classpath:config/,file:./config/:四個位置加載applicatin.properties或者application.yml。?
我們可以將EnvironmentPostProcessor作為一個動態管理配置文件的接口。?
EnvironmentPostProcessor的擴展
回到ConfigFileApplicationListener的onApplicationEnvironmentPreparedEvent方法,它獲取所有的EnvironmentPostProcessor來postProcessEnvironment。
1. SpringApplicationJsonEnvironmentPostProcessor?
? ? 從Env中現有的配置解析spring.application.json.如果有值,則向env中添加一個MapPropertySource。spring.applictaion.json的值可以通過命令行參數傳遞進來
2. CloudFoundryVcapEnvironmentPostProcessor?
? ? 從已經存在的env中尋找到VCAP
3. ConfigFileApplicationListener?
? 從配置路徑中加載application.properties/yml/xml
Spring Boot允許針對不同的環境配置不同的配置參數,可以使用 properties文件、YAML 文件、環境變量或者命令行參數來修改應用的配置。你可以在代碼中使用@Value注解來獲取配置參數的值
經過這一步加載過后env中包含的ProperytySource有:
設置輸出格式AnsiOutputApplicationListener
AnsiOutputApplicationListener 根據spring.output.ansi.enabled的值來配置AnsiOutput
完成日志系統的集成:LoggingApplicationListener
SpringBoot在這一步完成與日志系統的集成。項目中如果使用了spring boot starter則默認使用logback。我們可以使用自己需要用的日志。只需要將適當的庫添加到classpath,便激活各種日志系統。在classpath根目錄可以提供一個合適命名的配置文件來定制日志系統。我們不需要在application.properties中做額外的操作。?
spring的日志管理,寫的很贊的一篇?
在ApplicationStartedEvent事件中,LoggingApplicationListener尋找合適的日志系統?
在ApplicationEnvironmentPrepared事件中,LoggingApplicationListener尋找到配置文件并且初始化日志系統
加快應用初始化BackgroundPreinitializer
spring boot 為了加速應用的初始化,在后臺線程提前初始化一些耗時的任務
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {try {Thread thread = new Thread(new Runnable() {public void run() {this.runSafely(new BackgroundPreinitializer.MessageConverterInitializer(null));this.runSafely(new BackgroundPreinitializer.MBeanFactoryInitializer(null));this.runSafely(new BackgroundPreinitializer.ValidationInitializer(null));this.runSafely(new BackgroundPreinitializer.JacksonInitializer(null));this.runSafely(new BackgroundPreinitializer.ConversionServiceInitializer(null));}public void runSafely(Runnable runnable) {try {runnable.run();} catch (Throwable var3) {;}}}, "background-preinit");thread.start();} catch (Exception var3) {;}}創建和準備環境這一步做的事情總結如下:?
1. 加載配置文件?
2. 初始化日志系統?
3. 提前加載某些后臺的進程。在這些進程中創建一些消息轉換器等
打印Banner
Banners:持有多個Bannner,循環打印?
PrintBanner:持有一個Banner,打印?
Banner可以打印在Console,也可以打印到日志文件中。所以有不同的輸出路徑。print函數應該有一個參數為OutputStream,用來做不同的輸出方式?
感興趣的是使用?
AnsiOutput.toString(new Object[]{AnsiColor.GREEN, ” :: Spring Boot :: “, AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version})方法輸出有顏色的
創建應用上下文 createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext");} catch (ClassNotFoundException var3) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);}}return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);}如果當前是Web應用,則實例化AnnotationConfigEmbeddedWebApplicationContext,否則實例化AnnotationConfigApplicationContext;?
想要了解ApplicationContext的實例化過程中都做了什么,必須了解其類結構。
ApplicationContext有關的類結構
作為一個Context,ApplicationContext被賦予了很多功能。
? ? EnvironmentCapable,攜帶環境的
? ? ApplicationEventPublisher:發布事件
? ? SingletonBeanRegistry:注冊單例bean
? ? ListableBeanFactory:bean工廠
? ? DefaultResourceLoader:加載資源
? ? BeanDefinitionRegistry:注冊bean定義
我們追尋其構造方法,發現分別在AbstractApplicationContext和GenericApplicationContext和AnnotationConfigEmbeddedWebApplicationContext三個類中有構造方法。?
AbstractApplicationContext
1. 設置Log
2. 設置id和displayName
3. **初始化BeanFactoryPostProcessor的集合**
4. active,closed初始化
5. 初始化ApplicationListener的集合
6. 設置resourcePatternResolver
2. GenericApplicationContext
1. customClassLoader = false
2. refreshed = false
3. **beanFactory = new DefaultListableBeanFactory**
3. AnnotationConfigEmbeddedWebApplicationContext
1. **this.reader = new AnnotatedBeanDefinitionReader(this);**
2. **this.scanner = new ClassPathBeanDefinitionScanner(this);**
我們簡單介紹context中幾個比較重要的類
BeanFactoryPostProcessor,DefaultListableBeanFactory,?
AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner;
BeanFactoryPostProcessor
可以修改應用上下文的bean definition,以適應上下文底層bean工廠的屬性?
spring boot會給我們提供一些默認的PostProcessor
DefaultListableBeanFactory
BeanFactory
Bean容器的根接口。這個接口需要被含有許多bean definition(每一個bean definition有一個唯一的String名字來標識)的對象所實現。根據bean definition,bean容器會返回一個獨立的實例或者一個共享的單例。具體返回哪種,需要根據bean 容器的配置。這里說的類型就是Scope?
BeanFactory是應用組件的集中注冊器。我們在使用中最好使用DI(push推配置)來通過構造器或者settter方法來配置對象。而不是使用任何的pull配置來配置對象,比如通過一個BeanFactory查找。Spring的DI是被BeanFactory及其子類來實現的。?
和ListableBeanFactory相反的是,HierarachalBeanFactory會在查找parent factory。如果一個bean沒有查找到,會查找parent factory。子層次中的beans可以覆蓋父層次中的相同名字的bean.
Bean Factory的實現需要支持標準的bean 的生命周期。下面是全部的初始化方法和他們的排序。?
BeanNameAware’s setBeanName?
BeanClassLoaderAware’s setBeanClassLoader?
BeanFactoryAware’s setBeanFactory?
EnvironmentAware’s setEnvironment?
EmbeddedValueResolverAware’s setEmbeddedValueResolver?
ResourceLoaderAware’s setResourceLoader (only applicable when running in an application context)?
ApplicationEventPublisherAware’s setApplicationEventPublisher (only applicable when running in an application context)?
MessageSourceAware’s setMessageSource (only applicable when running in an application context)?
ApplicationContextAware’s setApplicationContext (only applicable when running in an application context)?
ServletContextAware’s setServletContext (only applicable when running in a web application context)?
postProcessBeforeInitialization methods of BeanPostProcessors?
InitializingBean’s afterPropertiesSet?
a custom init-method definition?
postProcessAfterInitialization methods of BeanPostProcessors?
在一個bean fatcory被關閉的時候,需要執行的生命周期的方法是:
postProcessBeforeDestruction methods of DestructionAwareBeanPostProcessors?
DisposableBean’s destroy?
a custom destroy-method definitions.?
BeanDefinitionRegistry?
注冊bean definition的接口。通常有BeanFactories來實現。Spring 的bean definition Reader需要使用這個接口。眾所周知的實現是DefaultListableBeanFactory 和GenericApplicationContext.
DefaultListableBeanFactory?
ListableBeanFactory和BeanDefinitionRegistry的實現。基于bean definition的bean factory.典型的用法是在獲取bean之前,注冊所有的bean definition。因此 bean definition的查找實在本地的集合中通過預先構建的bean definition metedata進行,是一項輕松的操作。?
可以被作為一個標準的bean factory,或者是一個自定義工廠的父類。特定類型的bean definition 格式的讀取器是獨立實現的,而不是作為bean factory的子類。比如 PropertiesBeanDefinitionReader和XmlBeanDefinitionReader
AnnotatedBeanDefinitionReader
顧名思義:讀取注解的bean definition的定義。?
在GenericWebApplicationContext中構造了它的一個實例??匆幌滤臉嬙旌瘮?。
我們可以看到AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);這個方法里面做了這幾件事情:?
1. 設置beanfactory的依賴比較器?
2. 設置beanfactory的autowire候選解析器?
3. 給beanfatory注冊幾個postprocessor的bean definition。
這幾個PostProcessor中含有一個特別重要的ConfigurationClassPostProcessor。就是這個類使用ConfigurationClassParser完成了basepackage目錄下的bean的掃描,并循環處理這些類(處理其中的內部類,methodbean,import,接口,importResource,ProportyResource類)。
//AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {registerAnnotationConfigProcessors(registry, (Object)null);}public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);...Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet(4);RootBeanDefinition def;if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) {//這里這里這里def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));}....return beanDefs;}private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {definition.setRole(2);registry.registerBeanDefinition(beanName, definition);return new BeanDefinitionHolder(definition, beanName);}ClassPathBeanDefinitionScanner
同樣,一個好的名字能夠讓人理解這個類的功能。?
ClassPathBeanDefinitionScanner是一個bean definition的掃描器,可以掃描類路徑下的候選的bean,并在給定的registry注冊bean definition。?
需要配置類型過濾器來發現候選bean。默認的filters包含@Component,@Repository,@Service,@Controller注解。同樣支持Java EE 6’s的ManagedBean和JSR-330’s Named 的注解
我們可以在BeanDefinitionDefaults發現一些東西。
public class BeanDefinitionDefaults {private boolean lazyInit;private int dependencyCheck = 0;private int autowireMode = 0;private String initMethodName;private String destroyMethodName; }初始化過程
至此,context的初始化過程完畢。我們回顧一下context在構造函數中做了什么?
AbstractApplicationContext?
1. 設置Log?
2. 設置id和displayName?
3.?初始化BeanFactoryPostProcessor的集合?
4. active,closed初始化?
5. 初始化ApplicationListener的集合?
6. 設置resourcePatternResolver?
GenericApplicationContext?
1. customClassLoader = false?
2. refreshed = false?
3.?beanFactory = new DefaultListableBeanFactory?
AnnotationConfigEmbeddedWebApplicationContext?
1.?this.reader = new AnnotatedBeanDefinitionReader(this);注冊了幾個BeanDefinition,最重要的是CongiurationClassPostProcessor的bean definition?
2.?this.scanner = new ClassPathBeanDefinitionScanner(this);設置了需要識別的注解的類型
失敗分析器
準備上下文prepareContext
我們初始化之后的ApplicationContext是最原始的狀態。需要配置之前創建好的Environment,SpringApplicationRunListeners,ApplicationArguments,Banner
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);//設置envthis.postProcessApplicationContext(context);//如果SpringApplication類的beanNameGenerator,resourceLoader不為null,則設置到context中去。//使用initializer做些初始化的動作//1. 調用用戶配置的initializer的方法//2. 設置context的Id//3. context注冊 ConfigurationWarningsPostProcessor用于檢查錯誤的配置//4. 向env中設置實際的端口//5. SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor//6. 設置ContextRefreshedEvent監聽器,打印ConditionEvaluationReport日志this.applyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);}Set<Object> sources = this.getSources();Assert.notEmpty(sources, "Sources must not be empty");this.load(context, sources.toArray(new Object[sources.size()]));listeners.contextLoaded(context);}使用initializer初始化context
protected void applyInitializers(ConfigurableApplicationContext context) {Iterator var2 = this.getInitializers().iterator();while(var2.hasNext()) {ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();//獲取initailizer的類型參數的類型Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");//使用initializer來初始化contextinitializer.initialize(context);}}| DelegatingApplicationContextInitializer | 從context中獲取env,從中獲取用戶是否配置了context.initializer.classes類。如果配置了這些類,則初始化他們并調用對應的initialize方法 |
| ContextIdApplicationContextInitializer | 從env中解析名字和端口號,最后兩者組合,設置為applicationContext的Id.我們在context的構造函數中給context設置的id值是context.toString即org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2b43529a。這一步之后,Id值為application:9188 |
| ConfigurationWarningsApplicationContextInitializer | 這個初始化器的作用在于向context中注冊ConfigurationWarningsPostProcessor,用于檢查錯誤的配置和報告錯誤 |
| ServerPortInfoApplicationContextInitializer | ApplicationContextInitializer向env設置EmbeddedServletContainer服務器實際監聽的端口。實現這個功能的方法是:向context添加一個ApplicationListener,監聽EmbeddedServletContainerInitializedEvent事件。當事件到達時,設置env中的local.server.port為實際端口值 |
| SharedMetadataReaderFactoryContextInitializer | ? |
| AutoConfigurationReportLoggingInitializer | 向applicationContext添加ApplicationListener用來監聽ContextRefreshedEvent或者ApplicationFailedEvent事件。當事件發生時,打印ConditionEvaluationReport日志到info級別,如果出錯,則打印到debug級別 |
總的來說:applyInitializers的所作的事情:?
1. 調用用戶配置的initializer的方法?
2. 設置context的Id?
3. context注冊 ConfigurationWarningsPostProcessor用于檢查錯誤的配置?
4. 向env中設置實際的端口?
5. SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor?
6. 設置ContextRefreshedEvent監聽器,打印ConditionEvaluationReport日志
向listeners廣播contextPrepared
EventPublishingSpringApplicationRunListener中什么都沒有做
打印啟動日志
if (this.logStartupInfo) {//使用StartupInfoLogger打印啟動信息.StartUpInfoLogger可以打印應用的starting,started的信息this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}protected void logStartupProfileInfo(ConfigurableApplicationContext context) {Log log = this.getApplicationLog();if (log.isInfoEnabled()) {String[] activeProfiles = context.getEnvironment().getActiveProfiles();if (ObjectUtils.isEmpty(activeProfiles)) {String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();log.info("No active profile set, falling back to default profiles: " + StringUtils.arrayToCommaDelimitedString(defaultProfiles));} else {log.info("The following profiles are active: " + StringUtils.arrayToCommaDelimitedString(activeProfiles));}}}打印結果為
INFO [11:57:12.206][main][org.springframework.boot.StartupInfoLogger][48]:Starting AppBootstrap on candydeMacBook-Pro.local with PID 19129 (/Users/maoyanyule/IdeaProjects/movie-pressurer/provider/target/classes started by maoyanyule in /Users/maoyanyule/IdeaProjects/movie-pressurer) INFO [11:58:25.218][main][org.springframework.boot.SpringApplication][593]:No active profile set, falling back to default profiles: default注冊兩個兩個單例bean
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);}此時context.singletonObject中有三個對象:?
1. springApplicationArguments?
2. springBootBanner?
3. autoConfigurationReport
將source加載到context中
protected void load(ApplicationContext context, Object[] sources) {if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}if (this.environment != null) {loader.setEnvironment(this.environment);}loader.load();}創建BeanDefinitionLoader,加載bean definition
創建bean definition Loader
BeanDefinitionLoaderbean 定義加載器??紤]bean definition的幾種方式。有注解和Xml類型,同時需要掃描主類所在的目錄。所以應該有AnnotatedBeanDefinitionReader,XmlBeanDefinitionReader
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {Assert.notNull(registry, "Registry must not be null");Assert.notEmpty(sources, "Sources must not be empty");this.sources = sources;this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);this.xmlReader = new XmlBeanDefinitionReader(registry);if (this.isGroovyPresent()) {this.groovyReader = new GroovyBeanDefinitionReader(registry);}this.scanner = new ClassPathBeanDefinitionScanner(registry);this.scanner.addExcludeFilter(new BeanDefinitionLoader.ClassExcludeFilter(sources));}AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner在前文已經解釋過?
XmlBeanDefinitionReader?加載XML文件,并委托BeanDefinitionDocumentReader讀取Xml文件,BeanDefinitionDocumentReader會在給定的bean fatcory注冊bean definition。
加載bean definition
調用BeanDefinitionLoader.load()方法
//循環加載所有的sources public int load() {int count = 0;Object[] var2 = this.sources;//這里的sources只有主類一個int var3 = var2.length;for(int var4 = 0; var4 < var3; ++var4) {Object source = var2[var4];count += this.load(source);}return count;}//根據不同的source類型使用不同的方法加載private int load(Object source) {Assert.notNull(source, "Source must not be null");if (source instanceof Class) {return this.load((Class)source);} else if (source instanceof Resource) {return this.load((Resource)source);} else if (source instanceof Package) {return this.load((Package)source);} else if (source instanceof CharSequence) {return this.load((CharSequence)source);} else {throw new IllegalArgumentException("Invalid source type " + source.getClass());}}//因為source為主類,所以是這個方法private int load(Class<?> source) {if (this.isGroovyPresent() && BeanDefinitionLoader.GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {BeanDefinitionLoader.GroovyBeanDefinitionSource loader = (BeanDefinitionLoader.GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, BeanDefinitionLoader.GroovyBeanDefinitionSource.class);this.load(loader);}//如果source被component注解,則使用AnnotatedBeanDefinitionReader來注冊source的bean definitionif (this.isComponent(source)) {this.annotatedReader.register(new Class[]{source});return 1;} else {return 0;}}我們來看AnnotatedBeanDefinitionReader是如何注冊一個source 的bean definition的
public void register(Class... annotatedClasses) {Class[] var2 = annotatedClasses;int var3 = annotatedClasses.length;for(int var4 = 0; var4 < var3; ++var4) {Class<?> annotatedClass = var2[var4];this.registerBean(annotatedClass);}}public void registerBean(Class<?> annotatedClass) {this.registerBean(annotatedClass, (String)null, (Class[])null);}public void registerBean(Class<?> annotatedClass, Class... qualifiers) {this.registerBean(annotatedClass, (String)null, qualifiers);}//主要是在這里public void registerBean(Class<?> annotatedClass, String name, Class... qualifiers) {//獲取一個Class的bean定義,初始獲取的bean definition。AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);//判斷是否應該跳過這次注冊if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {//從source的注解中獲取作用域,并設置到bean definition中ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());//獲取bean的名字String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);//處理bean definition中的一些屬性AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if (qualifiers != null) {Class[] var7 = qualifiers;int var8 = qualifiers.length;for(int var9 = 0; var9 < var8; ++var9) {Class<? extends Annotation> qualifier = var7[var9];if (Primary.class == qualifier) {abd.setPrimary(true);} else if (Lazy.class == qualifier) {abd.setLazyInit(true);} else {abd.addQualifier(new AutowireCandidateQualifier(qualifier));}}}//BeanDefinitionHolder中含有beanName,bean definitionBeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);//將作用域作用在bean definition 上definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);//在context中注冊bean definition BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}}在這里涉及到了 bean definition 的注冊。?
BeanDefinition的類圖?
我們從AbstaractBeanDefinition可以看出一些眼熟的東西:
在這一步完成之后,context中包含了一個和主類有關的bean definition.
廣播contextLoaded事件
public void contextLoaded(ConfigurableApplicationContext context) {ApplicationListener listener;//在spring boot initializ()方法中初始化的listeners直到這一步才被添加到context的listener中for(Iterator var2 = this.application.getListeners().iterator(); var2.hasNext(); context.addApplicationListener(listener)) {listener = (ApplicationListener)var2.next();//如果listener中需要context實例,則設置if (listener instanceof ApplicationContextAware) {((ApplicationContextAware)listener).setApplicationContext(context);}}//廣播ApplicationPreparedEvent事件this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));}| ConfigFileApplicationListener | ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent |
| LoggingApplicationListener | ApplicationStartingEvent,ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ContextClosedEvent,ApplicationFailedEvent |
| DelegatingApplicationListener | ApplicationEnvironmentPreparedEvent,** |
我們有三個listener關心ApplicationPreparedEvent事件?
ConfigFileApplicationListener?
向context中添加ConfigFileApplicationListener.PropertySourceOrderingPostProcessor?
LoggingApplicationListener?
向context中注冊單例bean:springBootLoggingSystem?
DelegatingApplicationListener繼續想其他listeners廣播事件
prepareContext回顧
刷新context refreshContext
private void refreshContext(ConfigurableApplicationContext context) {this.refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();} catch (AccessControlException var3) {;}}}protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);//最終調用了applicationContext的refresh()函數((AbstractApplicationContext)applicationContext).refresh();}而查看applicationContext.refresh()方法時需要注意其繼承結構。附上applicationContext的類圖?
為刷新context做準備: prepareRefresh()
//AnnotationConfigEmbeddedWebApplicationContext protected void prepareRefresh() {this.scanner.clearCache();super.prepareRefresh();//調用AbstractApplicationContext中的prepareRefresh();}應該記得在構造函數中創建的ClassPathBeanDefinitionScanner。?
在ClassPathBeanDefinitionScanner持有了一個Map:Resource, MetadataReader metadataReaderCache,用來保存resource和metadataReader;?
scanner.clearCache()即清空該map的元素
回顧env的類圖。查看代碼,可發現validateRequiredProperties是調用了AbstractProportyReslover.validateRequiredProperties()方法.作用是檢查是否缺失了必須配置的屬性?
回顧prepareRefresh()這一步的動作:?
1. 清空scanner中的resource和metadataReader之間的對應關系?
2. 設置applicationcontext中的初始狀態?
3. 打印啟動日志?
4. 設置env中的servletContext和serveltConfig?
5. 驗證是否缺失了必須配置的參數
獲取bean factory
//AbstractApplicationContext protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {this.refreshBeanFactory();//刷新bean factoryConfigurableListableBeanFactory beanFactory = this.getBeanFactory();//獲取beanFactoryif (this.logger.isDebugEnabled()) {this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);}return beanFactory;} //GenericApplicationContext.java protected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {//bean factory值可以刷新一次throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");} else {//設置beanFactory的serializationId和向//Map<String, Reference<DefaultListableBeanFactory>> serializableFactories添加serializationId, new WeakReference(this)this.beanFactory.setSerializationId(this.getId());}}準備bean factory
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {//設置類加載器beanFactory.setBeanClassLoader(this.getClassLoader());//BeanExpressionResolver的標準實現用來解析spring el表達式,可以使用#{bean.xxx}的方式來調用相關屬性值beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));//通過資源編輯器 操作一個給定的PropertyEditorRegistry.并在所有bean創建的過程中應用PropertyEditorRegistry.沒有用過?beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));//ApplicationContextAwareProcessor用于上下文回調,它是BeanPostProcessor的實現類,跟一下這個接口的兩個方法postProcessBeforeInitialization和postProcessAfterInitialization//如果Bean是EmbeddedValueResolverAware接口的實現類,則調用setEmbeddedValueResolver方法,傳入當前BeanFactory//如果Bean是ResourceLoaderAware接口的實現類,則調用setResourceLoader方法,傳入當前上下文ApplicationContext//如果Bean是ApplicationEventPublisherAware的實現類,則調用setApplicationEventPublisher方法,傳入當前上下文ApplicationContext//如果Bean是MessageSourceAware的實現類,則調用setMessageSource方法,傳入當前上下文ApplicationContext//如果Bean是ApplicationContextAware的實現類,則調用setApplicationContext方法,傳入當前上下文ApplicationContext//https://www.cnblogs.com/xrq730/p/6670457.htmlbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//定義了在依賴檢查和自動綁定時要忽略的依賴接口,是一組類對象,默認情況下,只有beanFactory接口被忽略。beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);//根據第二個參數注冊一個特殊的依賴類型,意思是修正依賴,這里是一些自動裝配的特殊規則,比如是BeanFactory接口的實現類,則修正為當前BeanFactorybeanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));if (beanFactory.containsBean("loadTimeWeaver")) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}//注冊enviroment單例if (!beanFactory.containsLocalBean("environment")) {beanFactory.registerSingleton("environment", this.getEnvironment());}//注冊systemProperties單例if (!beanFactory.containsLocalBean("systemProperties")) {beanFactory.registerSingleton("systemProperties", this.getEnvironment().getSystemProperties());}//注冊systemEnvironment單例if (!beanFactory.containsLocalBean("systemEnvironment")) {beanFactory.registerSingleton("systemEnvironment", this.getEnvironment().getSystemEnvironment());}}PostProcessorBeanFactory
//添加beanPostProcessor由于這里的basePackages為null,所以并不掃描 //這一步的作用主要是注冊BeanPostProcessors //AnnotationConfigEmbeddedWebApplicationContext.java protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);if (this.basePackages != null && this.basePackages.length > 0) {this.scanner.scan(this.basePackages);}if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {this.reader.register(this.annotatedClasses);}} //EmbeddedWebApplicationContext.java protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(ServletContextAware.class);}invokeBeanFactoryPostProcessors
主要是實例化和調用所有已注冊的BeanFactoryPostProcessors.需要在所有的bean實例化之前調用。?
這個是整個Spring流程中非常重要的一部分,是Spring留給用戶的一個非常有用的擴展點,BeanPostProcessor接口針對的是每個Bean初始化前后做的操作而BeanFactoryPostProcessor接口針對的是所有Bean實例化前的操作,
關于如何使用BeanFactoryPostProcessors作為擴展點,可以參考這篇博文容器擴展點BeanFactoryPostProcessors
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}這里委托了PostProcessorRegistrationDelegate工具類來調用BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {// Invoke BeanDefinitionRegistryPostProcessors first, if any.// 如果有BeanDefinitionRegistryPostProcessors 的話,就先調用它們// 處理過的BeansSet<String> processedBeans = new HashSet<String>();// 是否是BeanDefinitionRegistry類型的BeanFactory. BeanDefinitionRegistry的作用是可以用來注冊,刪除,獲取BeanDefinitionsif (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;// 普通類型的BeanFactoryPostProcessor集合List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();// BeanDefinitionRegistry類型的BeanFactoryPostProcessor集合(BeanDefinitionRegistryPostProcessor繼承于BeanFactoryPostProcessor)List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =new LinkedList<BeanDefinitionRegistryPostProcessor>();// 對集合beanFactoryPostProcessors進行分類,如果是BeanDefinitionRegistry類型的BeanFactoryPostProcessor;則調用方法 - postProcessBeanDefinitionRegistry()。// postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)該方法的作用是在標準的BeanDefinitions初始化完成后可以修改容器上下文內部的beanDefinition。for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryPostProcessor =(BeanDefinitionRegistryPostProcessor) postProcessor;registryPostProcessor.postProcessBeanDefinitionRegistry(registry);registryPostProcessors.add(registryPostProcessor);}else {regularPostProcessors.add(postProcessor);}}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the bean factory post-processors apply to them!// Separate between BeanDefinitionRegistryPostProcessors that implement// PriorityOrdered, Ordered, and the rest.// 這里不要初始化FactoryBeans,//我們需要保留這些普通的beans 不在這里初始化,目的是為了讓bean factory post-processor去處理他們。// 根據BeanDefinitionRegistryPostProcessors 實現的不同接口PriorityOrdered,Ordered,或者不實現兩者,拆分開來去調用他們。String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.// 首先,調用實現了優先級接口 - PriorityOrdered的BeanDefinitionRegistryPostProcessors List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}// 排序sortPostProcessors(beanFactory, priorityOrderedPostProcessors);// 添加排序好的優先級PostProcessors到registryPostProcessors集合registryPostProcessors.addAll(priorityOrderedPostProcessors);// 調用排序好的優先級BeanDefinitionRegistryPostProcessors,postProcessBeanDefinitionRegistry方法會被調用,用來修改,獲取,刪除容器上下文中的bean定義。//!!!!!這里是比較重要的一個點。priorityOrderedPostProcessors因為包含ConfigurationClassPostProcessor所以掃描了根目錄下的所有類,加載了對應的bean定義之所以從這個步驟中取到了 ConfigurationClassPostProcessor,是因為當初AnnotatedBeanDefinitionReader只注冊了bean definition。 invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.// 調用實現了Ordered接口的BeanDefinitionRegistryPostProcessors ,大致邏輯同上PriorityOrdered的處理postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(beanFactory, orderedPostProcessors);registryPostProcessors.addAll(orderedPostProcessors);invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry);// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.// 調用所有其它類型的BeanDefinitionRegistryPostProcessorsboolean reiterate = true;while (reiterate) {reiterate = false;postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName)) {BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);registryPostProcessors.add(pp);processedBeans.add(ppName);pp.postProcessBeanDefinitionRegistry(registry);reiterate = true;}}}// Now, invoke the postProcessBeanFactory callback of all processors handled so far.// 調用所有的BeanDefinitionRegistryPostProcessor的postProcessBeanFactory 方法。invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);// 調用容器中BeanFactoryPostProcessor類型的bean(不包含BeanDefinitionRegistryPostProcessor類型的beans)。invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);}else {// Invoke factory processors registered with the context instance.// 若beanFactory不是BeanDefinitionRegistry類型的,則直接調用容器中所有的BeanFactoryPostProcessor的postProcessBeanFactory 方法。invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the bean factory post-processors apply to them!String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,// Ordered, and the rest.//將BeanFactoryPostProcessors分為3類,PriorityOrdered,Ordered,其他List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();List<String> orderedPostProcessorNames = new ArrayList<String>();List<String> nonOrderedPostProcessorNames = new ArrayList<String>();for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {//如果前面已經處理過過postprocessor,則跳過// skip - already processed in first phase above}else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.//首先處理實現了PriorityOrdered接口的postProcessorsortPostProcessors(beanFactory, priorityOrderedPostProcessors);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);// Next, invoke the BeanFactoryPostProcessors that implement Ordered.//其次處理實現了Ordered接口的postprocessorList<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}sortPostProcessors(beanFactory, orderedPostProcessors);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);// Finally, invoke all other BeanFactoryPostProcessors.//最終調用其他的postprocessorList<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();for (String postProcessorName : nonOrderedPostProcessorNames) {nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);// Clear cached merged bean definitions since the post-processors might have// modified the original metadata, e.g. replacing placeholders in values...//postprocessor可能更改了原始數據的bean definition。所以清空緩存beanFactory.clearMetadataCache();}priorityOrderedPostProcessors因為包含ConfigurationClassPostProcessor所以掃描了根目錄下的所有類,加載了對應的bean定義。而在加載bean definition的時候,將自動配置的bean defintion也加載了進來。?
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
我們需要了解一下ConfigurationClassPostProcessor類的processConfigBeanDefinitions方法。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {//候選的待解析的含有@Configuration注解的類List<BeanDefinitionHolder> configCandidates = new ArrayList();//從現有的的bean definition中獲取待解析類的nameString[] candidateNames = registry.getBeanDefinitionNames();String[] var4 = candidateNames;int var5 = candidateNames.length;for(int var6 = 0; var6 < var5; ++var6) {String beanName = var4[var6];BeanDefinition beanDef = registry.getBeanDefinition(beanName);//如果當前的beanDef即不是full,也不是lite//在沒有checkConfigurationClassCandidate之前,beanDef的CONFIGURATION_CLASS_ATTRIBUTE屬性是沒有設置的if (!ConfigurationClassUtils.isFullConfigurationClass(beanDef) && !ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {//如果beanDef有@Configuration注解,則設置CONFIGURATION_CLASS_ATTRIBUTE為full,返回true//如果beanDef有Component,ComponentScan,Import,ImportResource注解中的一個,則CONFIGURATION_CLASS_ATTRIBUTE設置為lite,返回true//返回true,則加入configCandidatesif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}} else if (this.logger.isDebugEnabled()) {this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}if (!configCandidates.isEmpty()) {//首先對configCandidates進行排序Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return i1 < i2 ? -1 : (i1 > i2 ? 1 : 0);}});SingletonBeanRegistry sbr = null;if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry)registry;//如果當前沒有設置過beanNameGenerator,則設置if (!this.localBeanNameGeneratorSet && sbr.containsSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator")) {BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");this.componentScanBeanNameGenerator = generator;this.importBeanNameGenerator = generator;}}//創建ConfigurationClass的解析器ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);//待解析的configurationClass的bean defSet<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);//已經解析過的,元素為ConfigurationClassHashSet alreadyParsed = new HashSet(configCandidates.size());do {//解析parser.parse(candidates);parser.validate();//在解析的過程中會遇到很多的需要依賴的bean defSet<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);//去重if (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());}//加載這些依賴的bean defthis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();//如果registry中的bean def的數量比candidateNames要多,說明還有一些bean def沒有被處理。if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet();Iterator var12 = alreadyParsed.iterator();while(var12.hasNext()) {ConfigurationClass configurationClass = (ConfigurationClass)var12.next();alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}String[] var23 = newCandidateNames;int var24 = newCandidateNames.length;for(int var14 = 0; var14 < var24; ++var14) {String candidateName = var23[var14];//如果之前沒有遇到過這個候選的bean nameif (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);//如果這個類含有對應的注解,且沒有被解析過,則加入到candidates中if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}} while(!candidates.isEmpty());if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache();}}}我們需要關注的是其中的兩行代碼:parser.parse(candidates);用來解析候選類。在解析候選類的過程中會遇到很多以來的bean definition,this.reader.loadBeanDefinitions(configClasses);將注冊新發現的bean defintion
parser.parse(candidates);parser.validate();//在解析的過程中會遇到很多的需要依賴的bean defSet<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);//去重if (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());}//加載這些依賴的bean defthis.reader.loadBeanDefinitions(configClasses);ConfigurationClassParser用來解析一個beanDefition。因為在這次的場景中只有AppBootStrap這一個類。我們就從ConfigurationClassParser對這一個類的解析來說。
注冊beanPostProcessor
將beanPostProcessor分為4類,實現了PriorityOrdered接口的,實現了Ordered接口的,兩者都沒有實現的,是MergedBeanDefinitionPostProcessor的。這4類分別排序,被添加進入beanFactory的一個beanPostProcessor的list中,這里并不會調用BeanPostProcessor接口的方法。到時候在bean的初始化前后按順序調用他們。
這里就根據代碼總結一下BeanPostProcessor接口的調用順序:
initMessageSource用于創建messageSource
initApplicationEventMulticaster用于創建上下文事件廣播器
onRefresh創建Web容器
首先 onfresh():?
一個模板方法,重寫它的作用是添加特殊上下文刷新的工作,在特殊Bean的初始化時、初始化之前被調用。在Spring中,AbstractRefreshableWebApplicationContext、GenericWebApplicationContext、StaticWebApplicationContext都實現了這個方法。這三個類都是用它來配置themeSource。在EmbeddedWebApplicationContext中則創建和啟動了servlet容器。需要在bean實例化之前創建servletcontainer
我們在創建ServletContainer的時候,傳遞了一個initializer進去。在servlet容器創建后執行一些初始化動作
private void selfInitialize(ServletContext servletContext) throws ServletException {//我們需要重點關注這個方法this.prepareEmbeddedWebApplicationContext(servletContext);ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();EmbeddedWebApplicationContext.ExistingWebApplicationScopes existingScopes = new EmbeddedWebApplicationContext.ExistingWebApplicationScopes(beanFactory);WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.getServletContext());existingScopes.restore();WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.getServletContext());Iterator var4 = this.getServletContextInitializerBeans().iterator();while(var4.hasNext()) {ServletContextInitializer beans = (ServletContextInitializer)var4.next();beans.onStartup(servletContext);}}//關注這個方法的原因是我們在這個方法中設置了servletContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE屬性為我們之前applicationContext,也設置了我們applicationContext中的serveltContext屬性為在創建servlet容器時創建的servletContext protected void prepareEmbeddedWebApplicationContext(ServletContext servletContext) {Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);if (rootContext != null) {if (rootContext == this) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ServletContextInitializers!");}} else {Log logger = LogFactory.getLog(ContextLoader.class);servletContext.log("Initializing Spring embedded WebApplicationContext");try {servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);if (logger.isDebugEnabled()) {logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");}this.setServletContext(servletContext);if (logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - this.getStartupDate();logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");}} catch (RuntimeException var6) {logger.error("Context initialization failed", var6);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var6);throw var6;} catch (Error var7) {logger.error("Context initialization failed", var7);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var7);throw var7;}}}AfterFresh
spring boot在這一步中完成對Runners的調用?
參考CommandLineRunner,ApplicationRunner
參考資料
講bean definition的加載過程的更詳細的一篇博文?
spring boot源碼解析專欄?
PostProcessorRegistrationDelegate?
詳解ImportSelector接口?
關于springboot 的啟動流程?
BeanPostProcessor擴展點?
非懶加載bean初始化的過程,特別贊,作者還有關于spring的一系列的文章,建議可以去看?
refresh方法?
Jetty的工作原理?
Spring中的bean工廠后置處理器
總結
以上是生活随笔為你收集整理的springboot启动流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nginx 容器教程
- 下一篇: Go实战--也许最快的Go语言Web框架