Springboot啟動(dòng)原理
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|??eternal_heathens?
來源 |? urlify.cn/RFzYF3
66套java從入門到精通實(shí)戰(zhàn)課程分享?
public?static?void?main(String[] args)?{
????????//xxx.class:主配置類,(可以傳多個(gè))
????????SpringApplication.run(xxx.class, args);
????}1. 從run方法開始,創(chuàng)建SpringApplication,然后再調(diào)用run方法
/**
?* ConfigurableApplicationContext(可配置的應(yīng)用程序上下文)
?*/
public?static?ConfigurableApplicationContext run(Class> primarySource, String... args) {
????//調(diào)用下面的run方法
????return?run(new?Class[]{primarySource}, args);
}
public?static?ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
????//primarySources:主配置類
????return?(new?SpringApplication(primarySources)).run(args);
}2. 創(chuàng)建SpringApplication
public?SpringApplication(Class>... primarySources) {
????//調(diào)用下面構(gòu)造方法
????this((ResourceLoader) null, primarySources);
}public?SpringApplication(ResourceLoader resourceLoader, Class>... primarySources)?{
???//獲取類加載器
???this.resourceLoader = resourceLoader;
???//查看類加載器是否為空
???Assert.notNull(primarySources, "PrimarySources must not be null");
???// 保存主配置類的信息到一個(gè)哈希鏈表集合中,方便查詢調(diào)用增刪
???this.primarySources = new?LinkedHashSet<>(Arrays.asList(primarySources));
???//獲取當(dāng)前應(yīng)用的類型,是不是web應(yīng)用,見2.1
???this.webApplicationType = WebApplicationType.deduceFromClasspath();
???//從類路徑下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起來,見2.2
???setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
???//從類路徑下找到META‐INF/spring.factories配置的所有spring.ApplicationListener;然后保存起來,原理同上
???setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
???//從多個(gè)SpringApplication中找到有main方法的主SpringApplication(在調(diào)run方法的時(shí)候是可以傳遞多個(gè)配置類的)
???//只記錄有main方法的類名,以便下一步的run
???this.mainApplicationClass = deduceMainApplicationClass();
}2.1 deduceFromClasspath
static?WebApplicationType deduceFromClasspath() {
???if?(ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
?????????&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
??????return?WebApplicationType.REACTIVE;
???}
???for?(String className : SERVLET_INDICATOR_CLASSES) {
??????if?(!ClassUtils.isPresent(className, null)) {
?????????return?WebApplicationType.NONE;
??????}
???}
???return?WebApplicationType.SERVLET;
}2.2 getSpringFactoriesInstances
private? Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
???ClassLoader classLoader = getClassLoader();
???// Use names and ensure unique to protect against duplicates
???// 配置的所有ApplicationContextInitializer等分別到一個(gè)集合中以便查詢使用,其中l(wèi)oadFactoryNames方法從類路徑下找到META‐INF/spring.factories中傳入的parameterTypes
???Set<String> names = new?LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
???// 實(shí)例化傳入的類
???List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
???// 排序以便提高針對(duì)他執(zhí)行操作的效率
???AnnotationAwareOrderComparator.sort(instances);
???return?instances;
} 2.3 deduceMainApplicationClass
private?Class> deduceMainApplicationClass() {
???try?{
???????// 查詢當(dāng)前的虛擬機(jī)的當(dāng)前線程的StackTrace信息
??????StackTraceElement[] stackTrace = new?RuntimeException().getStackTrace();
??????for?(StackTraceElement stackTraceElement : stackTrace) {
??????????// 查看當(dāng)前線程中已加載的類中有沒有main方法
?????????if?("main".equals(stackTraceElement.getMethodName())) {
???????????//有則返回該類的類名
????????????return?Class.forName(stackTraceElement.getClassName());
?????????}
??????}
???}
???catch?(ClassNotFoundException ex) {
??????// Swallow and continue
???}
???return?null;
}StackTrace簡(jiǎn)述
1 StackTrace用棧的形式保存了方法的調(diào)用信息.
2 怎么獲取這些調(diào)用信息呢?
可用Thread.currentThread().getStackTrace()方法得到當(dāng)前線程的StackTrace信息.該方法返回的是一個(gè)StackTraceElement數(shù)組.
3 該StackTraceElement數(shù)組就是StackTrace中的內(nèi)容.
4 遍歷該StackTraceElement數(shù)組.就可以看到方法間的調(diào)用流程.
比如線程中methodA調(diào)用了methodB那么methodA先入棧methodB再入棧.
5 在StackTraceElement數(shù)組下標(biāo)為2的元素中保存了當(dāng)前方法的所屬文件名,當(dāng)前方法所屬的類名,以及該方法的名字
除此以外還可以獲取方法調(diào)用的行數(shù).
6 在StackTraceElement數(shù)組下標(biāo)為3的元素中保存了當(dāng)前方法的調(diào)用者的信息和它調(diào)用時(shí)的代碼行數(shù).
3. 調(diào)用SpringApplication對(duì)象的run方法
public?ConfigurableApplicationContext run(String... args) {
????????//Simple stop watch, allowing for timing of a number of tasks, exposing totalrunning time and running time for each named task.簡(jiǎn)單來說是創(chuàng)建ioc容器的計(jì)時(shí)器
????????StopWatch stopWatch = new?StopWatch();
????????stopWatch.start();
????????//聲明IOC容器
????????ConfigurableApplicationContext context = null;
????????//異常報(bào)告存儲(chǔ)列表
????????Collection exceptionReporters = new?ArrayList();
????????//加載圖形化配置
????????this.configureHeadlessProperty();
????????//從類路徑下META‐INF/spring.factories獲取SpringApplicationRunListeners,原理同2中獲取ApplicationContextInitializer和ApplicationListener
????????SpringApplicationRunListeners listeners = this.getRunListeners(args);
????????//遍歷上一步獲取的所有SpringApplicationRunListener,調(diào)用其starting方法
????????listeners.starting();
????????Collection exceptionReporters;
????????try?{
????????????//封裝命令行
????????????ApplicationArguments applicationArguments = new?DefaultApplicationArguments(args);
????????????//準(zhǔn)備環(huán)境,把上面獲取到的SpringApplicationRunListeners傳過去,見3.1
????????????ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
????????????
????????????this.configureIgnoreBeanInfo(environment);
????????????//打印Banner,就是控制臺(tái)那個(gè)Spring字符畫
????????????Banner printedBanner = this.printBanner(environment);
????????????//根據(jù)當(dāng)前環(huán)境利用反射創(chuàng)建IOC容器,見3.2
????????????context = this.createApplicationContext();
????????//從類路徑下META‐INF/spring.factories獲取SpringBootExceptionReporter,原理同2中獲取ApplicationContextInitializer和ApplicationListener
????????????exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new?Class[]{ConfigurableApplicationContext.class}, context);
????????????//準(zhǔn)備IOC容器,見3.3
????????????this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
????????????//刷新IOC容器,可查看配置嵌入式Servlet容器原理,所有的@Configuration和@AutoConfigutation等Bean對(duì)象全在此時(shí)加入容器中,并依據(jù)不同的選項(xiàng)創(chuàng)建了不同功能如服務(wù)器/數(shù)據(jù)庫等組件。見3.4
????????????//可以說@SpringbootApplication中自動(dòng)掃描包和Autoconfiguration也是在此時(shí)進(jìn)行的
????????????this.refreshContext(context);
????????????//這是一個(gè)空方法
????????????this.afterRefresh(context, applicationArguments);
????????????//停止計(jì)時(shí),打印時(shí)間
????????????stopWatch.stop();
????????????if?(this.logStartupInfo) {
????????????????(new?StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
????????????}
????????????//調(diào)用所有SpringApplicationRunListener的started方法
????????????listeners.started(context);
????????????//見3.5 ,從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進(jìn)行回調(diào)ApplicationRunner先回調(diào),CommandLineRunner再回調(diào)。
????????????this.callRunners(context, applicationArguments);
????????} catch?(Throwable var10) {
????????????this.handleRunFailure(context, var10, exceptionReporters, listeners);
????????????throw?new?IllegalStateException(var10);
????????}
????????try?{
????????????//調(diào)用所有SpringApplicationRunListener的running方法
????????????listeners.running(context);
????????????//返回創(chuàng)建好的ioc容器,啟動(dòng)完成
????????????return?context;
????????} catch?(Throwable var9) {
????????????this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
????????????throw?new?IllegalStateException(var9);
????????}
????} 3.1?prepareEnvironment
private?ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)?{
????????//獲取或者創(chuàng)建環(huán)境,有則獲取,無則創(chuàng)建
????????ConfigurableEnvironment environment = this.getOrCreateEnvironment();
????????//配置環(huán)境
????????this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
????????ConfigurationPropertySources.attach((Environment)environment);
????????//創(chuàng)建環(huán)境完成后,ApplicationContext創(chuàng)建前,調(diào)用前面獲取的所有SpringApplicationRunListener的environmentPrepared方法,讀取配置文件使之生效
????????listeners.environmentPrepared((ConfigurableEnvironment)environment);
????????// 環(huán)境搭建好后,需依據(jù)他改變Apllication的參數(shù),將enviroment的信息放置到Binder中,再由Binder依據(jù)不同的條件將“spring.main”SpringApplication更改為不同的環(huán)境,為后面context的創(chuàng)建搭建環(huán)境
????????//為什么boot要這樣做,其實(shí)咱們啟動(dòng)一個(gè)boot的時(shí)候并不是一定只有一個(gè)Application且用main方式啟動(dòng)。這樣子我們可以讀取souces配置多的Application
????????this.bindToSpringApplication((ConfigurableEnvironment)environment);
????????if?(!this.isCustomEnvironment) {
????????????environment = (new?EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
????????}
????????ConfigurationPropertySources.attach((Environment)environment);
????????//回到3,將創(chuàng)建好的environment返回
????????return?(ConfigurableEnvironment)environment;
????}3.2?createApplicationContext
protected?ConfigurableApplicationContext createApplicationContext() {
????????//獲取當(dāng)前Application中ioc容器類
????????Class> contextClass = this.applicationContextClass;
????????//若沒有則依據(jù)該應(yīng)用是否為web應(yīng)用而創(chuàng)建相應(yīng)的ioc容器
????????//除非為其傳入applicationContext,不然Application的默認(rèn)構(gòu)造方法并不會(huì)創(chuàng)建ioc容器
????????if?(contextClass == null) {
????????????try?{
????????????????switch(this.webApplicationType) {
????????????????case?SERVLET:
????????????????????contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
????????????????????break;
????????????????case?REACTIVE:
????????????????????contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
????????????????????break;
????????????????default:
????????????????????contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
????????????????}
????????????} catch?(ClassNotFoundException var3) {
????????????????throw?new?IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
????????????}
????????}
????//用bean工具類實(shí)例化ioc容器對(duì)象并返回。回到3
????????return?(ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
????}3.3?prepareContext
private?void?prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
????????//將創(chuàng)建好的環(huán)境放到IOC容器中
????????context.setEnvironment(environment);
????????//處理一些組件,沒有實(shí)現(xiàn)postProcessA接口
????????this.postProcessApplicationContext(context);
????????//獲取所有的ApplicationContextInitializer調(diào)用其initialize方法,這些ApplicationContextInitializer就是在2步驟中獲取的,見3.3.1
????????this.applyInitializers(context);
????????//回調(diào)所有的SpringApplicationRunListener的contextPrepared方法,這些SpringApplicationRunListeners是在步驟3中獲取的
????????listeners.contextPrepared(context);
????????//打印日志
????????if?(this.logStartupInfo) {
????????????this.logStartupInfo(context.getParent() == null);
????????????this.logStartupProfileInfo(context);
????????}
????????ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
????????//將一些applicationArguments注冊(cè)成容器工廠中的單例對(duì)象
????????beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
????????if?(printedBanner != null) {
????????????beanFactory.registerSingleton("springBootBanner", printedBanner);
????????}
????????if?(beanFactory instanceof?DefaultListableBeanFactory) {
????????????((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
????????}
????
????????//若是延遲加載,則在ioc容器中加入LazyInitializationBeanFactoryPostProcessor,
????????if?(this.lazyInitialization) {
????????????context.addBeanFactoryPostProcessor(new?LazyInitializationBeanFactoryPostProcessor());
????????}
????????//獲取所有報(bào)告primarySources在內(nèi)的所有source
????????Set<Object> sources = this.getAllSources();
????????Assert.notEmpty(sources, "Sources must not be empty");
????????//加載容器
????????this.load(context, sources.toArray(new?Object[0]));
????????//回調(diào)所有的SpringApplicationRunListener的contextLoaded方法
????????listeners.contextLoaded(context);
????}3.3.1?applyInitializers
protected?void?applyInitializers(ConfigurableApplicationContext context)?{
????????Iterator var2 = this.getInitializers().iterator();
????????while(var2.hasNext()) {
????????????//將之前的所有的ApplicationContextInitializer遍歷
????????????ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
????????????//解析查看之前從spring.factories調(diào)用的ApplicationContextInitializer是否能被ioc容器調(diào)用
????????????Class> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
????????????Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
????????????//可以調(diào)用則對(duì)ioc容器進(jìn)行初始化(還沒加載我們自己配置的bean和AutoConfiguration那些)
????????????initializer.initialize(context);
????????}
????}//返回3.33.4?refreshment(context)
@Override?
//詳情見內(nèi)置Servlet的啟動(dòng)原理,最后是用ApplicationContext的實(shí)現(xiàn)類的refresh()方法,若是web Application則為ServletWebServerApplicationContext
public?void?refresh()?throws?BeansException, IllegalStateException {
???synchronized?(this.startupShutdownMonitor) {
??????// Prepare this context for refreshing.
??????prepareRefresh();
??????// Tell the subclass to refresh the internal bean factory.
??????ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
??????// Prepare the bean factory for use in this context.
??????prepareBeanFactory(beanFactory);
??????try?{
?????????// Allows post-processing of the bean factory in context subclasses.
?????????postProcessBeanFactory(beanFactory);
?????????// Invoke factory processors registered as beans in the context.
?????????invokeBeanFactoryPostProcessors(beanFactory);
?????????// Register bean processors that intercept bean creation.
?????????registerBeanPostProcessors(beanFactory);
?????????// Initialize message source for this context.
?????????initMessageSource();
?????????// Initialize event multicaster for this context.
?????????initApplicationEventMulticaster();
?????????// Initialize other special beans in specific context subclasses.
?????????onRefresh();
?????????// Check for listener beans and register them.
?????????registerListeners();
?????????// Instantiate all remaining (non-lazy-init) singletons.
?????????finishBeanFactoryInitialization(beanFactory);
?????????// Last step: publish corresponding event.
?????????finishRefresh();
??????}
??????catch?(BeansException ex) {
?????????if?(logger.isWarnEnabled()) {
????????????logger.warn("Exception encountered during context initialization - "?+
??????????????????"cancelling refresh attempt: "?+ ex);
?????????}
?????????// Destroy already created singletons to avoid dangling resources.
?????????destroyBeans();
?????????// Reset 'active' flag.
?????????cancelRefresh(ex);
?????????// Propagate exception to caller.
?????????throw?ex;
??????}
??????finally?{
?????????// Reset common introspection caches in Spring's core, since we
?????????// might not ever need metadata for singleton beans anymore...
?????????resetCommonCaches();
??????}
???}
}3.5?callRunners
private?void?callRunners(ApplicationContext context, ApplicationArguments args) {
????????List<Object> runners = new?ArrayList();
????????//將ioc容器中的的ApplicationRunner和CommandLineRunner(),在創(chuàng)建ioc容器時(shí)創(chuàng)建的
????????runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
????????runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
????????AnnotationAwareOrderComparator.sort(runners);
????????Iterator var4 = (new?LinkedHashSet(runners)).iterator();
????
????????//調(diào)用ApplicationRunner和CommandLineRunner的run方法
????????while(var4.hasNext()) {
????????????Object?runner = var4.next();
????????????if?(runner instanceof?ApplicationRunner) {
????????????????this.callRunner((ApplicationRunner)runner, args);
????????????}
????????????if?(runner instanceof?CommandLineRunner) {
????????????????this.callRunner((CommandLineRunner)runner, args);
????????????}
????????}
????}配置在META-INF/spring.factories
ApplicationContextInitializer(在我們將enviroment綁定到application后可以用來創(chuàng)建不同類型的context)
SpringApplicationRunListener(在Application 創(chuàng)建/運(yùn)行/銷毀等進(jìn)行一些我們想要的特殊配置)
只需要放在ioc容器中
ApplicationRunner(加載沒有在ApplicationContext中的bean時(shí)讓bean能加載)
CommandLineRunner(命令行執(zhí)行器)
Application初始化組件測(cè)試
1.創(chuàng)建ApplicationContextInitializer和SpringApplicationRunListener的實(shí)現(xiàn)類,并在META-INF/spring.factories文件中配置
public?class?TestApplicationContextInitializer?implements?ApplicationContextInitializer?{
????@Override
????public?void?initialize(ConfigurableApplicationContext configurableApplicationContext)?{
????????System.out.println("TestApplicationContextInitializer.initialize");
????}
}public?class?TestSpringApplicationRunListener?implements?SpringApplicationRunListener?{
????@Override
????public?void?starting()?{
????????System.out.println("TestSpringApplicationRunListener.starting");
????}
????@Override
????public?void?environmentPrepared(ConfigurableEnvironment environment)?{
????????System.out.println("TestSpringApplicationRunListener.environmentPrepared");
????}
????@Override
????public?void?contextPrepared(ConfigurableApplicationContext context)?{
????????System.out.println("TestSpringApplicationRunListener.contextPrepared");
????}
????@Override
????public?void?contextLoaded(ConfigurableApplicationContext context)?{
????????System.out.println("TestSpringApplicationRunListener.contextLoaded");
????}
????@Override
????public?void?started(ConfigurableApplicationContext context)?{
????????System.out.println("TestSpringApplicationRunListener.started");
????}
????@Override
????public?void?running(ConfigurableApplicationContext context)?{
????????System.out.println("TestSpringApplicationRunListener.running");
????}
????@Override
????public?void?failed(ConfigurableApplicationContext context, Throwable exception)?{
????????System.out.println("TestSpringApplicationRunListener.failed");
????}
}org.springframework.context.ApplicationContextInitializer=\
cn.clboy.springbootprocess.init.TestApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
cn.clboy.springbootprocess.init.TestSpringApplicationRunListener啟動(dòng)報(bào)錯(cuò):說是沒有找到帶org.springframework.boot.SpringApplication和String數(shù)組類型參數(shù)的構(gòu)造器,給TestSpringApplicationRunListener添加這樣的構(gòu)造器
public?TestSpringApplicationRunListener(SpringApplication application,String[] args)?{
????}2.創(chuàng)建ApplicationRunner實(shí)現(xiàn)類和CommandLineRunner實(shí)現(xiàn)類,因?yàn)槭菑膇oc容器完成創(chuàng)建后中提取存放在里面的這兩個(gè)Runner,因此可以直接添加到容器中,最后callRunner使用
@Component
public?class?TestApplicationRunner?implements?ApplicationRunner?{
????@Override
????public?void?run(ApplicationArguments args)?throws?Exception {
????????System.out.println("TestApplicationRunner.run\t--->"+args);
????}
}@Component
public?class?TestCommandLineRunn?implements?CommandLineRunner?{
????@Override
????public?void?run(String... args)?throws?Exception {
????????System.out.println("TestCommandLineRunn.runt\t--->"+ Arrays.toString(args));
????}
}粉絲福利:108本java從入門到大神精選電子書領(lǐng)取
???
?長(zhǎng)按上方鋒哥微信二維碼?2 秒 備注「1234」即可獲取資料以及 可以進(jìn)入java1234官方微信群
感謝點(diǎn)贊支持下哈?
