更新時(shí)間:2022-06-28 10:16:46 來源:動(dòng)力節(jié)點(diǎn) 瀏覽1804次
我們來看看SpringBoot啟動(dòng)的全過程。
@SpringBootApplication
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class, args);
}
}
SpringBoot 應(yīng)用注解是@Configuration、@Enable AutoConfiguration 和@ComponentScan 注解的集成。它們代表Spring bean的配置bean,開啟自動(dòng)配置spring的上下文,組件掃描的路徑。這就是為什么 * 應(yīng)用程序。Java需要放在根路徑下,所以@ComponentScan掃描就是整個(gè)項(xiàng)目。
其次,啟動(dòng)類默認(rèn)只有一個(gè)main方法,調(diào)用SpringApplication。運(yùn)行方法。讓我們看一下 SpringApplication 類。
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[]{source}, args);
}
...
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
Return (new Spring Application (sources). run (args); //sources is the specific CmsApplication. class class class class
}
...
通過提取兩個(gè)直接調(diào)用的run方法,可以看到靜態(tài)方法SpringApplication。run 最后創(chuàng)建一個(gè) SpringApplication 并在其中運(yùn)行 run 方法。
查看構(gòu)造方法:
public SpringApplication(Object... sources) {
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.initialize(sources);
}
...
設(shè)置基值后調(diào)用initialize方法初始化構(gòu)造函數(shù),如下:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = this.deduceWebEnvironment();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
...
(1)將source放在SpringApplication的source屬性中,source是一個(gè)LinkedHashSet(),也就是說我們可以同時(shí)創(chuàng)建多個(gè)自定義的非重復(fù)應(yīng)用,但是目前只有一個(gè)。
(2)判斷是否為web程序(?javax.servlet.Servlet且org.springframework.web.context.ConfigurableWebApplicationContextAll必須存在于類加載器中并設(shè)置為webEnvironmentAttribute.
(3)從spring中找到Application Context Initializer。工廠并將其設(shè)置為初始化程序。
(4)從spring中找到Application Listener。工廠并將其實(shí)例化為 Spring Application 的 listener listeners 屬性。這個(gè)過程是找到所有的應(yīng)用程序事件監(jiān)聽器。
(5)找出主方法類(這里是CmsApplication),返回Class對(duì)象。
默認(rèn)情況下,initialize 方法從 spring 中查找鍵為 ApplicationContextInitializer 的類。工廠文件:
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
關(guān)鍵是ApplicationListener。
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch ();// Construct a Task Execution Observer
StopWatch. start ();// Start execution, record start time
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
// Get Spring Application RunListeners with only one Event Publishing RunListener inside
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// Encapsulate Spring Application Event events and broadcast them to listeners in Spring Application to start listening
listeners.starting();
try {
// Construct an application parameter holder class
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// Load Configuration Environment
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
Banner printedBanner = this.printBanner(environment);
// Create Spring containers (using BeanUtils. instantiate)
context = this.createApplicationContext();
// If container creation fails, analyze the cause of output failure
new FailureAnalyzers(context);
// Setting up container configuration environment, monitoring, etc.
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// Refresh containers
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
// Broadcast the Application ReadyEvent event to the corresponding listener for execution
listeners.finished(context, (Throwable)null);
StopWatch. stop ();// End of execution, record execution time
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
Return context; // Return Spring container
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
run方法的流程分析如下。該方法的幾個(gè)關(guān)鍵步驟如下:
(1)創(chuàng)建應(yīng)用監(jiān)聽Spring Application RunListeners并開始監(jiān)聽
(2)加載SpringBook可配置環(huán)境。如果通過 Web 容器發(fā)布,它會(huì)加載標(biāo)準(zhǔn)環(huán)境,最終繼承可配置環(huán)境。類圖如下。

可以看到,*Environment最終實(shí)現(xiàn)了PropertyResolver接口,而我們通常在通過環(huán)境對(duì)象獲取配置文件中Key對(duì)應(yīng)的value方法時(shí),會(huì)調(diào)用PropertyResolver接口的getProperty方法。
(3)配置環(huán)境添加到監(jiān)聽器對(duì)象(Spring Application RunListeners)
(4)創(chuàng)建Spring Container:可配置應(yīng)用上下文(Application Configuration Context),我們可以看看創(chuàng)建方法
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);
}
該方法首先獲取顯式設(shè)置的應(yīng)用上下文類,如果不存在,則加載默認(rèn)環(huán)境配置(通過判斷是否為web環(huán)境),默認(rèn)Annotation Config應(yīng)用上下文注解上下文(通過掃描所有注解類加載bean) ,最后通過BeanUtils實(shí)例化上下文對(duì)象,并返回,Configurab。leApplicationContext類圖如下

主要取決于其繼承的兩個(gè)方向:
LifeCycle:生命周期類,定義了start start、stop、isRunning是否運(yùn)行一個(gè)中等生命周期的空值方法
ApplicationContext:應(yīng)用上下文類,主要繼承beanFactory類。
(5)回到run方法,設(shè)置容器prepareContext方法,將監(jiān)聽器、環(huán)境、應(yīng)用Arguments、banner等重要組件與上下文對(duì)象關(guān)聯(lián)起來。
(6)刷新容器。refresh()方法:初始化方法如下:
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
refresh()Method做了很多核心工作,比如設(shè)置BeanFactory,執(zhí)行BeanFactory PostProcessor接口,執(zhí)行BeanFactory Processor接口,解析自動(dòng)化配置類,加載spring。工廠,實(shí)例化bean,解析條件注釋,國(guó)際化初始化等等。這部分將在以后的文章中分析。
(7)廣播Application ReadyEvent并在執(zhí)行結(jié)束時(shí)返回Configurable Application Context。
至此,SpringBoot啟動(dòng)完成,回顧一下整體流程,Springboot啟動(dòng),主要?jiǎng)?chuàng)建配置環(huán)境(environment),監(jiān)聽器,應(yīng)用上下文,根據(jù)以上條件,我們開始實(shí)例化我們需要的bean容器。如果大家想了解更多相關(guān)知識(shí),可以關(guān)注一下動(dòng)力節(jié)點(diǎn)的Spring框架教程,里面有更豐富的知識(shí)等著大家去學(xué)習(xí),希望對(duì)大家能夠有所幫助。
Java實(shí)驗(yàn)班
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
Java就業(yè)班
有基礎(chǔ) 直達(dá)就業(yè)
Java夜校直播班
業(yè)余時(shí)間 高薪轉(zhuǎn)行
Java在職加薪班
工作1~3年,加薪神器
Java架構(gòu)師班
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)