SpringApplication 在各種賦值都完成, 初始化部分已完成逊移,下一步是根據(jù)初始化的各種配置,對(duì)應(yīng)用進(jìn)行啟動(dòng),包括bean的加載,配置文件的讀入等等, 調(diào)用的方法為:
springApplication.run(args);
源碼(版本為Spring-boot-1.3.0.M5)如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
context = doRun(listeners, args);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(
getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
try {
listeners.finished(context, ex);
this.log.error("Application startup failed", ex);
}
finally {
if (context != null) {
context.close();
}
}
ReflectionUtils.rethrowRuntimeException(ex);
return context;
}
}
1. StopWatch 監(jiān)控
用來記錄任務(wù)的啟動(dòng) 結(jié)束時(shí)間,是一個(gè)非線程的安全的萤悴,如果自己使用要考慮多線程的情況.
2. 配置Headless模式, configureHeadlessProperty
private void configureHeadlessProperty() {
System.setProperty(
SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
Boolean.toString(this.headless)));
}
用來設(shè)置java.awt.headless 屬性是true 還是false, java.awt.headless是J2SE的一種模式用于在缺少顯示屏、鍵盤或者鼠標(biāo)時(shí)的系統(tǒng)配置更啄,很多監(jiān)控工具如jconsole 需要將該值設(shè)置為true.
3. SpringApplicationRunListener 對(duì)ApplicationListener事件處理
在初始化的時(shí)候 我們加載了一批 ApplicationListener,包括springboot自啟動(dòng)的 ,也可以有用戶定義ApplicationListener,SpringApplicationRunListener就負(fù)責(zé)來加載ApplicationListener中的業(yè)務(wù).
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
void started();
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes.
* @param context the application context
* @param exception any run exception or null if run completed successfully.
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
ApplicationListener可以實(shí)現(xiàn)四中event類型, 分別在不同的時(shí)候加載, ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ApplicationFailedEvent , ApplicationStartedEvent.
在run的方法中分別使用在:
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
listeners.environmentPrepared(environment);
listeners.contextPrepared(context);
listeners.finished(context, null);
4. doRun 方法實(shí)現(xiàn)
private ConfigurableApplicationContext doRun(SpringApplicationRunListeners listeners,
String... args) {
ConfigurableApplicationContext context;
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, args);
listeners.environmentPrepared(environment);
if (this.showBanner) {
printBanner(environment);
}
// Create, load, refresh and run the ApplicationContext
context = createApplicationContext();
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
}
// Add boot specific singleton beans
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
// Refresh the context
refresh(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
return context;
}```
#####a. 創(chuàng)建和配置環(huán)境相關(guān)
i. 創(chuàng)建 environment
首先根據(jù)初始化的配置創(chuàng)建environment的類型, 后面或者property 和profile都是通過這個(gè)類來獲取的,然后configureEnvironment 就是用來Property和Profiled的.
ii. 啟動(dòng)說監(jiān)聽器的environmentPrepared方法,會(huì)調(diào)用所有監(jiān)聽器中監(jiān)聽ApplicationEnvironmentPreparedEvent的方法,具體邏輯在上一步中已經(jīng)描述了.
iii. printBanner
打印啟動(dòng)的banner,工業(yè)化的項(xiàng)目中可以對(duì)此進(jìn)行定制,使用Banner類即可.
#####b. 創(chuàng)建 加載 刷新 運(yùn)行ApplicationContext
i.創(chuàng)建context
```java
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class
.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS
: DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
根據(jù)是否是webEnvironment 創(chuàng)建不同的context,是創(chuàng)建的是AnnotationConfigEmbedded,WebApplicationContext, 不是創(chuàng)建的默認(rèn)的 AnnotationConfig, ApplicationContext.
ii.注冊(cè)關(guān)閉的鉤子稚疹,主要實(shí)現(xiàn)當(dāng)JVM關(guān)閉時(shí)候,做一些關(guān)閉處理居灯,如銷毀bean祭务,生命周期關(guān)閉等, 實(shí)現(xiàn)方式是
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
JVM關(guān)閉時(shí)會(huì)自動(dòng)調(diào)用.
iii. initializer 初始化 日志啟動(dòng)等其他操作.
c. 配置spring boot特有的單例bean 如commandline相關(guān)
d.加載資源怪嫌,就是在初始化過程中source包含的類
e.context 重新刷新
這個(gè)主要都走spring 框架的AbstractApplicationContext#refresh义锥,是對(duì)context中的各種資源進(jìn)行加載, 由于不是boot特有,這里不仔細(xì)描述
f.監(jiān)聽器實(shí)現(xiàn)所有finished操作
主要的實(shí)現(xiàn)ApplicationReadyEvent 或者 ApplicationFailedEvent.
至此岩灭,springboot啟動(dòng)過程分析完成了.
5. 后記
本身主要研究分析了boot本身的代碼的情況拌倍,并沒有對(duì)spring的代碼 和 每個(gè)監(jiān)聽器功能進(jìn)行深入分析后面可開專門文章對(duì)此進(jìn)行補(bǔ)充 ,方便不熟悉spring的同學(xué)理解.