通過(guò)代碼實(shí)例了解SpringBoot啟動(dòng)原理
這篇文章主要介紹了通過(guò)代碼實(shí)例了解SpringBoot啟動(dòng)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
SpringBoot和Spring相比,有著不少優(yōu)勢(shì),比如自動(dòng)配置,jar直接運(yùn)行等等。那么SpringBoot到底是怎么啟動(dòng)的呢?
下面是SpringBoot啟動(dòng)的入口:
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
一、先看一下@SpringBoot注解:
@Target({ElementType.TYPE}) //定義其使用時(shí)機(jī)
@Retention(RetentionPolicy.RUNTIME) //編譯程序?qū)nnotation儲(chǔ)存于class檔中,可由VM使用反射機(jī)制的代碼所讀取和使用。
@Documented //這個(gè)注解應(yīng)該被 javadoc工具記錄
@Inherited //被注解的類會(huì)自動(dòng)繼承. 更具體地說(shuō),如果定義注解時(shí)使用了 @Inherited 標(biāo)記,然后用定義的注解來(lái)標(biāo)注另一個(gè)父類, 父類又有一個(gè)子類(subclass),則父類的所有屬性將被繼承到它的子類中.
@SpringBootConfiguration //@SpringBootConfiguration就相當(dāng)于@Configuration。JavaConfig配置形式
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)} //自動(dòng)掃描并加載符合條件的組件。以通過(guò)basePackages等屬性來(lái)細(xì)粒度的定制@ComponentScan自動(dòng)掃描的范圍,如果不指定,則默認(rèn)Spring框架實(shí)現(xiàn)會(huì)從聲明@ComponentScan所在類的package進(jìn)行掃描。
注:所以SpringBoot的啟動(dòng)類最好是放在root package下,因?yàn)槟J(rèn)不指定basePackages。
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
所以,實(shí)際上SpringBootApplication注解相當(dāng)于三個(gè)注解的組合,@SpringBootConfiguration,@ComponentScan和@EnableAutoConfiguration。
@SpringBootConfiguration和@ComponentScan,很容易知道它的意思,一個(gè)是JavaConfig配置,一個(gè)是掃描包。關(guān)鍵在于@EnableAutoConfiguration注解。先來(lái)看一下這個(gè)注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
Springboot應(yīng)用啟動(dòng)過(guò)程中使用ConfigurationClassParser分析配置類時(shí),如果發(fā)現(xiàn)注解中存在@Import(ImportSelector)的情況,就會(huì)創(chuàng)建一個(gè)相應(yīng)的ImportSelector對(duì)象, 并調(diào)用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata), 這里 EnableAutoConfigurationImportSelector的導(dǎo)入@Import(EnableAutoConfigurationImportSelector.class) 就屬于這種情況,所以ConfigurationClassParser會(huì)實(shí)例化一個(gè) EnableAutoConfigurationImportSelector 并調(diào)用它的 selectImports() 方法。
AutoConfigurationImportSelector implements DeferredImportSelector extends ImportSelector。
下面是AutoConfigurationImportSelector的執(zhí)行過(guò)程:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NO_IMPORTS = new String[0];
private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
public AutoConfigurationImportSelector() {
}
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 從配置文件中加載 AutoConfigurationMetadata
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 獲取所有候選配置類EnableAutoConfiguration
// 使用了內(nèi)部工具使用SpringFactoriesLoader,查找classpath上所有jar包中的
// META-INF\spring.factories,找出其中key為
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
// 的屬性定義的工廠類名稱。
// 雖然參數(shù)有annotationMetadata,attributes,但在 AutoConfigurationImportSelector 的
// 實(shí)現(xiàn) getCandidateConfigurations()中,這兩個(gè)參數(shù)并未使用
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 應(yīng)用 exclusion 屬性
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 應(yīng)用過(guò)濾器AutoConfigurationImportFilter,
// 對(duì)于 spring boot autoconfigure,定義了一個(gè)需要被應(yīng)用的過(guò)濾器 :
// org.springframework.boot.autoconfigure.condition.OnClassCondition,
// 此過(guò)濾器檢查候選配置類上的注解@ConditionalOnClass,如果要求的類在classpath
// 中不存在,則這個(gè)候選配置類會(huì)被排除掉
configurations = this.filter(configurations, autoConfigurationMetadata);
// 現(xiàn)在已經(jīng)找到所有需要被應(yīng)用的候選配置類
// 廣播事件 AutoConfigurationImportEvent
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = this.getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> {
return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";
});
return attributes;
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
public abstract class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
public SpringFactoriesLoader() {
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
}
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
// 記錄候選配置類是否需要被排除,skip為true表示需要被排除,全部初始化為false,不需要被排除
boolean[] skip = new boolean[candidates.length];
// 記錄候選配置類中是否有任何一個(gè)候選配置類被忽略,初始化為false
boolean skipped = false;
Iterator var8 = this.getAutoConfigurationImportFilters().iterator();
// 獲取AutoConfigurationImportFilter并逐個(gè)應(yīng)用過(guò)濾
while(var8.hasNext()) {
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
// 對(duì)過(guò)濾器注入其需要Aware的信息
this.invokeAwareMethods(filter);
// 使用此過(guò)濾器檢查候選配置類跟autoConfigurationMetadata的匹配情況
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for(int i = 0; i < match.length; ++i) {
if (!match[i]) {
// 如果有某個(gè)候選配置類不符合當(dāng)前過(guò)濾器,將其標(biāo)記為需要被排除,
// 并且將 skipped設(shè)置為true,表示發(fā)現(xiàn)了某個(gè)候選配置類需要被排除
skip[i] = true;
skipped = true;
}
}
}
if (!skipped) {
// 如果所有的候選配置類都不需要被排除,則直接返回外部參數(shù)提供的候選配置類集合
return configurations;
} else {
// 邏輯走到這里因?yàn)閟kipped為true,表明上面的的過(guò)濾器應(yīng)用邏輯中發(fā)現(xiàn)了某些候選配置類
// 需要被排除,這里排除那些需要被排除的候選配置類,將那些不需要被排除的候選配置類組成
// 一個(gè)新的集合返回給調(diào)用者
List<String> result = new ArrayList(candidates.length);
int numberFiltered;
for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
if (!skip[numberFiltered]) {
result.add(candidates[numberFiltered]);
}
}
if (logger.isTraceEnabled()) {
numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList(result);
}
}
/**
* 使用內(nèi)部工具 SpringFactoriesLoader,查找classpath上所有jar包中的
* META-INF\spring.factories,找出其中key為
* org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
* 的屬性定義的過(guò)濾器類并實(shí)例化。
* AutoConfigurationImportFilter過(guò)濾器可以被注冊(cè)到 spring.factories用于對(duì)自動(dòng)配置類
* 做一些限制,在這些自動(dòng)配置類的字節(jié)碼被讀取之前做快速排除處理。
* spring boot autoconfigure 缺省注冊(cè)了一個(gè) AutoConfigurationImportFilter :
* org.springframework.boot.autoconfigure.condition.OnClassCondition
**/
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
二、下面看一下SpringBoot啟動(dòng)時(shí)run方法執(zhí)行過(guò)程
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
this.configureHeadlessProperty();
//從類路徑下META-INF/spring.factories獲取
SpringApplicationRunListeners listeners = getRunListeners(args);
//回調(diào)所有的獲取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封裝命令行參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//準(zhǔn)備環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//創(chuàng)建環(huán)境完成后回調(diào)
SpringApplicationRunListener.environmentPrepared();表示環(huán)境準(zhǔn)備完成
this.configureIgnoreBeanInfo(environment);
//打印Banner圖
Banner printedBanner = printBanner(environment);
//創(chuàng)建ApplicationContext,決定創(chuàng)建web的ioc還是普通的ioc
context = createApplicationContext();
//異常分析報(bào)告
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//準(zhǔn)備上下文環(huán)境,將environment保存到ioc中
//applyInitializers():回調(diào)之前保存的所有的ApplicationContextInitializer的initialize方法
//listeners.contextPrepared(context)
//prepareContext運(yùn)行完成以后回調(diào)所有的SpringApplicationRunListener的contextLoaded()
this.prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新容器,ioc容器初始化(如果是web應(yīng)用還會(huì)創(chuàng)建嵌入式的Tomcat)
//掃描,創(chuàng)建,加載所有組件的地方,(配置類,組件,自動(dòng)配置)
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//所有的SpringApplicationRunListener回調(diào)started方法
listeners.started(context);
//從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進(jìn)行回調(diào),
//ApplicationRunner先回調(diào),CommandLineRunner再回調(diào)
this.callRunners(context, applicationArguments);
}
catch (Throwable ex) {
this.handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//所有的SpringApplicationRunListener回調(diào)running方法
listeners.running(context);
}
catch (Throwable ex) {
this.handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//整個(gè)SpringBoot應(yīng)用啟動(dòng)完成以后返回啟動(dòng)的ioc容器
return context;
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
springboot整合kaptcha生成驗(yàn)證碼功能
這篇文章主要介紹了springboot整合kaptcha生成驗(yàn)證碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
Java 進(jìn)程執(zhí)行外部程序造成阻塞的一種原因
前一陣子在研究文檔展示時(shí)使用了java進(jìn)程直接調(diào)用外部程序,其中遇到一個(gè)問(wèn)題花了好長(zhǎng)時(shí)間才解決,這個(gè)問(wèn)題就是外部程序直接執(zhí)行沒(méi)什么問(wèn)題,但是當(dāng)使用Java進(jìn)程執(zhí)行時(shí)外部程序就阻塞在那兒不動(dòng)了。而且這個(gè)外部程序在處理某些文件時(shí)使用Java進(jìn)程執(zhí)行是沒(méi)問(wèn)題的2014-03-03
StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別
今天小編就為大家分享一篇關(guān)于StringUtils工具包中字符串非空判斷isNotEmpty和isNotBlank的區(qū)別,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
如何利用NetworkInterface獲取服務(wù)器MAC地址
今天介紹一種通用的跨平臺(tái)的操作方式,那就是JDK自帶的NetworkInterface接口,該接口在JDK1.4已經(jīng)出現(xiàn),但是功能比較少,JDK1.6之后新增了不少新功能,比較不錯(cuò)2013-08-08
Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng)的示例代碼
這篇文章主要介紹了如何利用Java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng),可以實(shí)現(xiàn)存款,取款,查詢等功能,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-09-09
Java的Jackson庫(kù)的使用及其樹(shù)模型的入門學(xué)習(xí)教程
這篇文章主要介紹了Java的Jackson庫(kù)的使用及其樹(shù)模型入門學(xué)習(xí)教程,Jackson庫(kù)通常被用來(lái)作Java對(duì)象和JSON的互相轉(zhuǎn)換,需要的朋友可以參考下2016-01-01

