Spring Boot: 啟動過程分析
建立Spring Boot專案
建立Spring Boot
專案非常簡單,只需要以下幾步
設定當前專案的parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
新增外掛
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.0.6.RELEASE</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
新增依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
最後建立一個類、並啟動它:
@SpringBootApplication public class MainApp { public static void main(String[] args) { SpringApplication.run(MainApp.class, args); } }
這樣一個Spring Boot
專案就建立完成了。但是,它的啟動過程是怎樣的?接著往下看。
啟動過程
最簡單的啟動Spring Boot
的方式就是這一行程式碼
SpringApplication.run(MainApp.class, args);
也可以使用SpringApplicationBuilder
或者使用SpringApplication
的構造方法來更靈活的應用Spring Boot
,甚至可以對SpringApplication
進行適當的擴充套件。
跟蹤程式碼發現,run方法內部是構造了一個SpringApplication例項然後再呼叫該例項的run方法。
構造SpringApplication例項
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//為primarySources屬性賦值
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//確定web app的型別,REACTIVE、NONE、SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//從spring.factories載入並例項化所有ApplicationContextInitializer的實現類
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//從spring.factories載入並例項化所有ApplicationListener的實現類
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//確定呼叫main方法的那個類,目前是MainApp類
this.mainApplicationClass = deduceMainApplicationClass();
}
從spring.factories
檔案可以看到,對於ApplicationContextInitializer
,配置了以下幾個實現
# /spring-boot-2.0.6.RELEASE.jar!/META-INF/spring.factories
# 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.web.context.ServerPortInfoApplicationContextInitializer
#/spring-boot-autoconfigure-2.0.6.RELEASE.jar!/META-INF/spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
對於ApplicationListener
,配置了以下幾個實現
# /spring-boot-2.0.6.RELEASE.jar!/META-INF/spring.factories
# 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.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# /spring-boot-autoconfigure-2.0.6.RELEASE.jar!/META-INF/spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
run方法
看下run方法的實現
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//SpringApplication的run方法的監聽器。從spring.factories載入並例項化所有SpringApplicationRunListener的實現類
//目前為止spring.factories中配置的SpringApplicationRunListener的實現只有EventPublishingRunListener,並且也僅有這一個實現類
SpringApplicationRunListeners listeners = getRunListeners(args);
//釋出ApplicationStartingEvent事件
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//建立environment並呼叫EnvironmentPostProcessor#postProcessEnvironment進行配置檔案讀取
//釋出ApplicationEnvironmentPreparedEvent事件給ApplicationListener進行配置檔案讀取
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//根據this.webApplicationType載入並例項化對應的ApplicationContext實現類,當前專案是AnnotationConfigApplicationContext
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);
//呼叫ApplicationContextInitializer#initialize
//將MainApp例項註冊到beanFactory
//釋出ApplicationPreparedEvent事件
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//ApplicationContext#refresh
refreshContext(context);
//空方法,子類可以進行擴充套件
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//釋出ApplicationStartedEvent事件
listeners.started(context);
//呼叫ApplicationRunner、CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//釋出ApplicationReadyEvent事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
SpringApplicationRunListener
Spring Boot
要求,SpringApplicationRunListener
的實現必須有一個接受SpringApplication application, String[] args
兩個引數的構造器。
看EventPublishingRunListener
的構造器
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
在這裡,構造SpringApplication
時從spring.factories
檔案載入的ApplicationListener
都被新增到initialMulticaster
屬性中去了。
類SimpleApplicationEventMulticaster
在refresh
時還會用到,它的作用就相當於是一系列ApplicationListener
的代理,當有事件發生時,直接傳送給ApplicationEventMulticaster
就行。
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
建立並初始化ConfigurableEnvironment
例項。
建立ConfigurableEnvironment
例項完成後會發佈一個ApplicationEnvironmentPreparedEvent
事件來呼叫相應的ApplicationListener
對Environment
做進一步的處理。
其中有一個ConfigFileApplicationListener
,以這個類為例。
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
看看處理ApplicationEnvironmentPreparedEvent
事件的邏輯。
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
//從spring.factories檔案中載入並例項化EnvironmentPostProcessor的實現類
//postProcessors = {[email protected]} size = 3
//0 = {[email protected]}
//1 = {[email protected]}
//2 = {[email protected]}
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
//ConfigFileApplicationListener也是一個EnvironmentPostProcessor的實現類
postProcessors.add(this);
//排序
AnnotationAwareOrderComparator.sort(postProcessors);
//呼叫EnvironmentPostProcessor的postProcessEnvironment方法
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
具體怎樣載入配置檔案就在這個ConfigFileApplicationListener
的postProcessEnvironment
方法了。
最終載入使用的是從spring.factories
檔案載入的PropertySourceLoader
的兩個例項進行的,分別用於處理.properties/.xml
檔案和.yml
檔案。
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
context = createApplicationContext();
這裡建立了ApplicationContext
例項,其實是AnnotationConfigServletWebServerApplicationContext
的例項。
AnnotationConfigServletWebServerApplicationContext
的無參構造器及其父類GenericApplicationContext
的無參構造器
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
this.scanner = new ClassPathBeanDefinitionScanner(this);
這行程式碼沒什麼好說的。關鍵是看
this.reader = new AnnotatedBeanDefinitionReader(this);
跟蹤這行程式碼,最終發現這裡註冊了以下一個bean到beanFactory例項。
//處理@Configuration、@Bean、@Service、@Component
org.springframework.context.annotation.internalConfigurationAnnotationProcessor:ConfigurationClassPostProcessor
//處理依賴注入,@Autowired、@Value,支援@Inject
org.springframework.context.annotation.internalAutowiredAnnotationProcessor:AutowiredAnnotationBeanPostProcessor
//處理@Required
org.springframework.context.annotation.internalRequiredAnnotationProcessor:RequiredAnnotationBeanPostProcessor
//處理@PostConstruct、@PreDestroy、@Resource、@WebServiceRef、@EJB
org.springframework.context.annotation.internalCommonAnnotationProcessor:CommonAnnotationBeanPostProcessor
//處理@EventListener
org.springframework.context.event.internalEventListenerProcessor:EventListenerMethodProcessor
org.springframework.context.event.internalEventListenerFactory:DefaultEventListenerFactory
其中,ConfigurationClassPostProcessor
是BeanFactoryPostProcessor
的實現,同時也實現了BeanDefinitionRegistryPostProcessor
;
AutowiredAnnotationBeanPostProcessor
、RequiredAnnotationBeanPostProcessor
、CommonAnnotationBeanPostProcessor
是BeanPostProcessor
的實現。
後面refresh時會用到這幾個類。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
//呼叫ApplicationContextInitializer#initialize
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 註冊applicationArguments到beanFactory
context.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
// this.primarySources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//使用BeanDefinitionLoader將MainApp例項註冊到beanFactory
load(context, sources.toArray(new Object[0]));
//將SpringApplication中的ApplicationListener例項新增到ApplicationContext中,併發布ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
目前為止事件的釋出都是使用的EventPublishingRunListener#initialMulticaster
來進行的。
後面的事件釋出都是用AbstractApplicationContext#applicationEventMulticaster
來進行。
實際上,這兩個屬性是SimpleApplicationEventMulticaster
的不同例項。
refreshContext(context);
這裡實際上呼叫了ApplicationContext
的refresh
方法,進行bean的掃描、註冊、例項化以及應用容器(tomcat、netty、jetty等)的建立、啟動等工作,內容較多,留在下一篇文章裡詳細說明。
callRunners(context, applicationArguments);
這裡也是Spring Boot
留下的一個擴充套件點,在啟動完成後執行一些自定義的任務。
總結
Spring Boot 的啟動過程如下:
- 構造SpringApplication例項 - 確定應用型別 - 載入並例項化ApplicationContextInitializer,在context建立之後、refresh之前對context做一些處理 - 載入並例項化ApplicationListener,用於處理ApplicationEvent
- 呼叫run方法 - 載入並例項化SpringApplicationRunListener對啟動過程進行監聽 - 建立ConfigurableEnvironment例項 - 建立ApplicationContext例項(同時也建立了BeanFactory例項) - 使用ApplicationContextInitializer對ApplicationContext做處理 - 將MainApp註冊到beanFactory - 呼叫ApplicationContext的refresh方法 - 呼叫ApplicationRunner、CommandLineRunner
Spring Boot的五個擴充套件點
- SpringApplication
- ApplicationContextInitializer
- ApplicationListener
- SpringApplicationRunListener
- ApplicationRunner、CommandLineRunner