springboot原理索引
SpringBoot源碼分析(1)–@SpringBootApplication注解使用和原理/SpringBoot的自動配置原理詳解
SpringBoot源碼分析(2)–SpringBoot啟動源碼(萬字圖文源碼debug講解springboot啟動原理)
一、前言
本文主要講解@SpringBootApplication注解使用和原理。
源碼基于spring-boot-2.2.13.RELEASE進(jìn)行講解
主要是弄懂以下幾個(gè)問題:
1.@SpringBootApplication注解主要做了什么事?
2.為什么能實(shí)現(xiàn)自動加載
3.SpringBoot怎么知道哪些java類應(yīng)該當(dāng)作Bean被注入到IOC容器中?
4.springboot默認(rèn)掃描啟動類同包及子包下面的組件,原理是什么?
如圖:為什么springboot能自動注入service, controller等注解到IOC容器中
二、主啟動類的配置
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
//@SpringBootApplication 來標(biāo)注一個(gè)主程序類
//說明這是一個(gè)Spring Boot應(yīng)用
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//以為是啟動了一個(gè)方法,實(shí)際是啟動了一個(gè)服務(wù)
SpringApplication.run(SpringbootApplication.class, args);
}
}
2.1、@SpringBootApplication注解
@SpringBootApplication內(nèi)部的組成結(jié)構(gòu),如下圖:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
..........
}
@SpringBootApplication注解是Spring Boot的核心注解,它其實(shí)是一個(gè)組合注解主要是由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三個(gè)注解合并而成。
@SpringBootConfiguration:
內(nèi)部結(jié)構(gòu)是@Configuration注解,標(biāo)明當(dāng)前類是一個(gè)配置類,項(xiàng)目啟動時(shí)該類會被加載到spring容器中
@EnableAutoConfiguration
開啟自動配置功能,實(shí)現(xiàn)自動裝配的核心注解,內(nèi)部包含兩個(gè)注解:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)。
@AutoConfigurationPackage:將主程序類所在包及所有子包下的組件到掃描到spring容器中。這里的組件主要是@Enitity、@Mapper等第三方依賴的注解
@Import(AutoConfigurationImportSelector.class):在該類中加載 META-INF/spring.factories 的配置信息。然后篩選出以 EnableAutoConfiguration 為 key 的數(shù)據(jù),加載到 IOC 容器中,實(shí)現(xiàn)自動配置功能!
@ComponentScan
默認(rèn)掃描@ComponentScan注解所在包及子包中標(biāo)注了@Component注解的類以及衍生注解標(biāo)注的類(如 @Repository or @Service or @Controller等等)
2.2、@SpringBootConfiguration注解
點(diǎn)進(jìn)@SpringBootConfiguration內(nèi)部,看其內(nèi)部結(jié)構(gòu):
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
// 相當(dāng)于@SpringBootConfiguration就是@Configuration
public @interface SpringBootConfiguration {
}
@Configuration是Spring的一個(gè)注解,標(biāo)明該類為配置類,其修飾的類會加入Spring容器。這就說明SpringBoot的啟動類會加入Spring容器。
驗(yàn)證啟動類是否被注入到spring容器中
@SpringBootApplication
public class Demo3Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Demo3Application.class, args);
Demo3Application application = run.getBean("demo3Application",Demo3Application.class);
System.out.println(application);
System.out.println("spring容器中是否包含啟動類:"+run.containsBean("demo3Application"));
}
}
可以看到以下執(zhí)行結(jié)果中,確實(shí)將啟動類加載到spring容器中了
2.3、@ComponentScan 注解
用于定義 Spring 的掃描路徑,等價(jià)于在 xml 文件中配置 context:component-scan,假如不配置掃描路徑,那么 Spring 就會默認(rèn)掃描當(dāng)前類所在的包及其子包中的所有標(biāo)注了 @Component,@Service,@Controller 等注解的類。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)//可重復(fù)注解
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};//基礎(chǔ)包名,等同于basePackages
@AliasFor("value")
String[] basePackages() default {};//要掃描的路徑,如果為空,解析的時(shí)候會解析被@ComponentScan標(biāo)注類的包路徑
Class<?>[] basePackageClasses() default {};//掃描的類,會掃描該類所在包及其子包的組件。與basePackages互斥
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;//注冊為BeanName生成策略 默認(rèn)BeanNameGenerator,用于給掃描到的Bean生成BeanName,在解析注冊BeanDefinition的時(shí)候用到
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;//用于解析bean的scope的屬性的解析器,默認(rèn)是AnnotationScopeMetadataResolver,類定義上的@Scope注解解析器,如果沒有該注解默認(rèn)單例
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;//scoped-proxy 用來配置代理方式 // no(默認(rèn)值):如果有接口就使用JDK代理,如果沒有接口就使用CGLib代理 interfaces: 接口代理(JDK代理) targetClass:類代理(CGLib代理)
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;//配置要掃描的資源的正則表達(dá)式的,默認(rèn)是"**/*.class",即配置類包下的所有class文件。
boolean useDefaultFilters() default true;//useDefaultFilters默認(rèn)是true,掃描@Component標(biāo)注的類以及衍生注解標(biāo)注的類(如@Component or @Repository or @Service or @Controller等等),如果為false則不掃描,需要自己指定includeFilters
Filter[] includeFilters() default {};//自定義包含過濾器,如果@Component掃描不到或者不能滿足,則可以使用自定義掃描過濾器
Filter[] excludeFilters() default {};//自定義排除過濾器,和includeFilters作用相反
boolean lazyInit() default false;//是否是懶加載
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {//過濾器注解
FilterType type() default FilterType.ANNOTATION;//過濾判斷類型
@AliasFor("classes")
Class<?>[] value() default {};//要過濾的類,等同于classes
@AliasFor("value")
Class<?>[] classes() default {};//要過濾的類,等同于value
String[] pattern() default {};// 正則化匹配過濾
}
}
@ComponentScan 注解解析與路徑掃描
@ComponentScan注解的解析依舊在SpringApplication調(diào)用AbstractApplicationContext#refresh的時(shí)候觸發(fā)調(diào)用ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry,然后通過ConfigurationClassParser#parse解析,委托給doProcessConfigurationClass方法處理:
具體原理可參考《@ComponentScan注解使用和原理》
2.4、@EnableAutoConfiguration注解
@EnableAutoConfiguration自動配置的關(guān)鍵,內(nèi)部實(shí)際上就去加載 META-INF/spring.factories 文件的信息,然后篩選出以 EnableAutoConfiguration 為key的數(shù)據(jù),加載到IOC容器中,實(shí)現(xiàn)自動配置功能
查看其內(nèi)部結(jié)構(gòu)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自動配置包
@AutoConfigurationPackage
// 使用@Import注解導(dǎo)入AutoConfigurationImportSelector類,實(shí)現(xiàn)了ImportSelector接口,重寫了selectImports()方法,幫助我們返回所有需要被注冊為bean的類全限定類名的數(shù)組集合
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
重點(diǎn)看@AutoConfigurationPackage注解和@Import(AutoConfigurationImportSelector.class)注解。
2.4.1、@AutoConfigurationPackage注解
@AutoConfigurationPackage作用:
將主程序類所在包及所有子包下的組件到掃描到spring容器中。這里的組件主要是@Enitity、@Mapper等第三方依賴的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
看其@Import進(jìn)來的類AutoConfigurationPackages.Registrar類:
這是一個(gè)內(nèi)部類,源碼如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
// 注冊bean定義
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
// 返回一組需要注入的對象
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//如果這個(gè)bean已經(jīng)注冊了,就獲取構(gòu)造函數(shù)參數(shù)值,并add包名
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
}
//如果沒有注冊,就創(chuàng)建一個(gè)新的bean定義并注冊
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
通過debug發(fā)現(xiàn)@AutoConfigurationPackage其實(shí)就是把主啟動類的包名傳進(jìn)來,然后將主啟動類同包及子包下的組件注冊到容器中。
2.4.2、@Import(AutoConfigurationImportSelector.class)
@EnableAutoConfiguration注解通過@Import(AutoConfigurationImportSelector.class)向容器中導(dǎo)入了AutoConfigurationImportSelector組件,它實(shí)現(xiàn)了DeferredImportSelector,并間接實(shí)現(xiàn)了ImportSelector接口,重寫了selectImports()方法,幫助我們返回所有需要被注冊為bean的類全限定類名的數(shù)組集合。
EnableAutoConfigurationImportSelector: 導(dǎo)入哪些組件的選擇器,將所有需要導(dǎo)入的組件以全類名的方式返回,這些組件就會被添加到容器中。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//......省略其他代碼
/**
* 返回一個(gè)Class全路徑的String數(shù)組,返回的Class被容器管理
*
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//①判斷是否需要導(dǎo)入 判斷 spring.boot.enableautoconfiguration有沒有開啟,默認(rèn)開啟(是否進(jìn)行自動裝配)
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//②獲取metadata配置信息
//加載配置文件META-INF/spring-autoconfigure-metadata.properties,從中獲取所有支持自動配置類的條件
//作用:SpringBoot使用一個(gè)Annotation的處理器來收集一些自動裝配的條件,那么這些條件可以在META-INF/spring-autoconfigure-metadata.properties進(jìn)行配置。
// SpringBoot會將收集好的@Configuration進(jìn)行一次過濾進(jìn)而剔除不滿足條件的配置類
// 自動配置的類全名.條件=值
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//③自動裝配
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
//......
}
/**
* ①判斷自動配置是否開啟
* 可以通過在配置文件中修改spring.boot.enableautoconfiguration為false來關(guān)閉自動配置。
*/
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
/**
* ②加載metadata配置信息
* 返回AutoConfigurationMetadata給第③步
*/
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
static AutoConfigurationMetadata loadMetadata(Properties properties) {
return new PropertiesAutoConfigurationMetadata(properties);
}
/**
* ③自動裝配
* 返回AutoConfigurationEntry
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//檢查是否自動裝配
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//獲取自動加載配置,spring.factories下以EnableAutoConfiguration為key的自動配置類
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//配置類去重
configurations = removeDuplicates(configurations);
//獲取被排除的類集合
// 若不希望某些自動配置類自動配置,可通過EnableAutoConfiguration的exclude或excludeName屬性進(jìn)行配置,
// 也可在配置文件里通過配置項(xiàng)“spring.autoconfigure.exclude”進(jìn)行配置。
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//檢查排除類是否合法(exclusions指定的類必須是自動配置類,否則拋出異常)
checkExcludedClasses(configurations, exclusions);
//從 configurations 中,移除所有不希望自動配置的配置類
configurations.removeAll(exclusions);
//過濾自動加載組件
//篩選候選的自動配置類,根據(jù)項(xiàng)目pom.xml中加入的依賴篩選出最終符合當(dāng)前項(xiàng)目運(yùn)行環(huán)境對應(yīng)的自動配置類
// 判斷是否要加載某個(gè)類的兩種方式:
// 根據(jù)spring-autoconfigure-metadata.properties進(jìn)行判斷。
// 判斷@Conditional是否滿足
configurations = filter(configurations, autoConfigurationMetadata);
// 過濾完成后會自動加載類路徑下Jar包中META-INF/spring.factories文件中 AutoConfigurationImportListener的實(shí)現(xiàn)類,
// 并觸發(fā)fireAutoConfigurationImportEvents事件。
//將配置類和排除類通過事件傳入監(jiān)聽器中,監(jiān)聽器的配置在于 spring.factories 文件中,通過 AutoConfigurationImportlistener 指定
fireAutoConfigurationImportEvents(configurations, exclusions);
//返回自動配置類全限定名數(shù)組
return new AutoConfigurationEntry(configurations, exclusions);
}
重點(diǎn)是 getCandidateConfigurations
方法,會給容器中注入眾多的自動配置類(xxxAutoConfiguration),就是給容器中導(dǎo)入這個(gè)場景需要的所有組件,并配置好這些組件。getCandidateConfigurations會到classpath下的讀取META-INF/spring.factories文件的配置,并返回以 EnableAutoConfiguration 為 key 的字符串?dāng)?shù)組。
getCandidateConfigurations默認(rèn)加載的是spring-boot-autoconfigure依賴包下的META-INF/spring.factories文件,我們可以看一個(gè)以EnableAutoConfiguration為key剛好127個(gè)
為什么getCandidateConfigurations讀取的是classpath下的META-INF/spring.factories文件?我們可以看一下源碼,如下圖所示,源碼中寫明了要讀取該文件。
簡單梳理:
- FACTORIES_RESOURCE_LOCATION 的值是 META-INF/spring.factories
- Spring啟動的時(shí)候會掃描所有jar路徑下的 META-INF/spring.factories ,將其文件包裝成Properties對象,從Properties對象獲取到key值為 EnableAutoConfiguration 的數(shù)據(jù),然后添加到容器里邊。
三、問題解答
針對@SpringBootApplication注解使用和原理過程中有一些疑問點(diǎn),特此解答
1.@AutoConfigurationPackage和@ComponentScan的作用是否沖突
起因
在研究springboot啟動類時(shí)看到了兩個(gè)意義相同的注解:@AutoConfigurationPackage和@ComponentScan,都是導(dǎo)入啟動類同包及子包下的組件,于是疑惑為什么要重復(fù)使用?
回答
@AutoConfigurationPackage和@ComponentScan一樣,都是將Spring Boot啟動類所在的包及其子包里面的組件掃描到IOC容器中,但是區(qū)別是@AutoConfigurationPackage掃描@Enitity、@Mapper等第三方依賴的注解,@ComponentScan 注解主要是掃描 Spring 家族的各種 Bean,如 @Controller、@Service、@Component、@Repository 以及由此衍生出來的一些其他的 Bean。所以這兩個(gè)注解掃描的對象是不一樣的。當(dāng)然這只是直觀上的區(qū)別,更深層次說,@AutoConfigurationPackage是自動配置的提醒,是Spring Boot中注解,而@ComponentScan是Spring的注解
2.為什么能實(shí)現(xiàn)自動加載
起因
springboot為什么能夠?qū)崿F(xiàn)自動加載,如數(shù)據(jù)庫,tomcat等默認(rèn)不用配置什么東西就能加載進(jìn)來。
回答
@EnableAutoConfiguration注解是自動配置的關(guān)鍵,內(nèi)部實(shí)際上就去加載 META-INF/spring.factories 文件的信息,然后篩選出以 EnableAutoConfiguration 為key的數(shù)據(jù),加載到IOC容器中,實(shí)現(xiàn)自動配置功能。
默認(rèn)加載的是spring-boot-autoconfigure依賴包下的META-INF/spring.factories文件,我們可以看一個(gè)以EnableAutoConfiguration為key的元素文章來源:http://www.zghlxwxcb.cn/news/detail-537364.html
本文只講解@SpringBootApplication注解使用和原理,內(nèi)部還有很多細(xì)節(jié)性的東西將在后面文章中具體分析,如AutoConfigurationImportSelector.class中的自動裝配是在哪里調(diào)用起來的?文章來源地址http://www.zghlxwxcb.cn/news/detail-537364.html
到了這里,關(guān)于SpringBoot源碼分析(1)--@SpringBootApplication注解使用和原理/SpringBoot的自動配置原理詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!