研究SpringBoot的自動裝配,同時也想弄明白它的啟動流程,然后就有了這篇隨筆。
SpringBoot的啟動一般都是從main方法開始,這也是它的第一步
//SpringBoot注解,用於啟動的
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//啟動類靜態的run方法
SpringApplication.run(DemoApplication.class, args);
}
}
第二步就是通過構造方法在創建SpringApplication對象時,會初始化對象的值
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
//定義標志輸出模式
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//通過調用deduceFromClasspath方法判斷web應用是什么類型的
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//將啟動注冊初始化類通過工廠類得到實例放到ArrayList列表
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//將應用上下文初始化類通過工廠類得到實例進行初始化
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//ApplicationListener監聽器類通過工廠類得到實例放進監聽器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//得到main方法類實例
this.mainApplicationClass = this.deduceMainApplicationClass();
}
第三步就是會執行對應的run方法,run方法源碼為:
public ConfigurableApplicationContext run(String... args) {
//獲取啟動時的時間毫秒數
long startTime = System.nanoTime();
//創建一個啟動上下文容器
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
//設置Headless模式屬性,Headless模式模式代表當缺少外設依賴時使用此模式
this.configureHeadlessProperty();
//創建監聽器,監聽器開始啟動
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//創建一個應用屬性對象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//創建應用的上下文環境,從配置的application.properties等配置文件中讀取
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//用來忽略所有自定義的BeanInfo類的搜索
this.configureIgnoreBeanInfo(environment);
//打印banner標志
Banner printedBanner = this.printBanner(environment);
//創建應用程序上下文
context = this.createApplicationContext();
//設置應用啟動器
context.setApplicationStartup(this.applicationStartup);
//對bean,屬性,標志進行初始化生成和處理
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//進行上下文刷新工作,如對系統關閉周期鈎子重置內容等等
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
//得到啟動時長
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
//告訴那個應用啟動用了多少秒
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
//啟動完成通知
listeners.started(context, timeTakenToStartup);
//如果有ApplicationRunner或者CommandLineRunner類型的bean,就會觸發調用
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
return context;
} catch (Throwable var11) {
this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var11);
}
}
在上面的方法中有這一行代碼 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); 這一行代碼涉及到了環境處理,屬性設置等等。針對涉及到的this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);方法進行解析
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//根據web應用類型選擇響應的應用容器環境
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//進行環境參數配置,但是此方法先去查看是否需要轉換服務,再有條件地進行環境參數配置
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
//通過判斷環境中的是否含有onfigurationProperties的屬性進行不同的處理
ConfigurationPropertySources.attach((Environment)environment);
//環境初始化,加載生效的profile等資源
listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
//配置屬性中添加defaultProperties默認配置
DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
//判斷環境屬性中是否包含spring.main.environment-prefix鍵,如果包含的話,則會報出異常,異常內容第二個參數
Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
//將配置屬性與各種組件進行綁定
this.bindToSpringApplication((ConfigurableEnvironment)environment);
//判斷是否需要進行容器類型轉換
if (!this.isCustomEnvironment) {
environment = this.convertEnvironment((ConfigurableEnvironment)environment);
}
//通過判斷環境中的是否含有onfigurationProperties的屬性進行不同的處理
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
在上面run方法的源碼中有這行代碼this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);,查看這個方法源碼
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
//應用程序對自定義的加載器進行加載
this.postProcessApplicationContext(context);
//初始化上下文
this.applyInitializers(context);
//調用監聽器
listeners.contextPr epared(context);
//往下追的話也是調用各種監聽器
bootstrapContext.close(context);
//日志輸出,輸出了一行JDK信息和進程號,以及生效的profile模式內容
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
//創建一個bean工廠
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//利用工廠進行注冊
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
//進行懶加載
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
//加載資源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}