SpringBoot中自動配置原理解析
前言
SpringBoot作為當(dāng)前Java開發(fā)的熱門框架,有絞手架之稱。“約定大于配置”也一直是SpringBoot的標(biāo)簽,那么,SpringBoot要實現(xiàn)自身優(yōu)勢,自動配置功不可沒。
一、SpringBoot自動配置是什么
SpringBoot自動配置是指在應(yīng)用程序啟動時,SpringBoot根據(jù)classpath路徑下的jar包自動配置應(yīng)用程序所需的一系列bean和組件,從而減少開發(fā)者的配置工作,提高開發(fā)效率。
二、@Import注解
在剖析SpringBoot自動配置原理之前,我們先了解一下@Import注解的使用
1. 方式一: .class方式
定義兩個類A、B,并將其加入到Spring IOC容器中:
@Data
public class A{
private Integer id=0;
private String name="classA";
public void print(){
System.out.println(this.name);
}
}
@Data
public class B{
private Integer id=1;
private String name="classB";
public void print(){
System.out.println(this.name);
}
}
創(chuàng)建一個配置類,并使用@Import注解將類A、B添加到 IOC 容器中:
@Import({A.class,B.class})
@Configuration
public class ClassConfig{
???????}
2.方式二:ImportSelector方式
該方法需要定義類來實現(xiàn)ImportSelector接口,并重寫其中的selectImports( )方法,該方法的返回值是需要添加到IOC容器中的類的全限定類名數(shù)組:
@Data
public class C{
private Integer id=2;
private String name="classC";
public void print(){
System.out.println(this.name);
}
}
編寫類來實現(xiàn)ImportSelector接口:
public class ImportSelectorTest implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata annotationMetada){
return new String[]{"C.class"};
}
}
使用該類:
@Import({ImportSelectorTest.class})
@Configuration
public class ImportSelectorConfig{
???????}
3.方式三:ImportBeanDefinitionRegistrar方式
定義一個類并實現(xiàn)ImportBeanDefinitionRegistrar接口,重寫其中的registerBeanDefinitions( )方法,此方式可以自定義Bean在容器中的名稱:
@Data
public class D{
private Integer id=3;
private String name="ClassD";
public void print(){
System.out.println(this.name);
}
}
//定義類實現(xiàn)ImPortBeanDefinitionRegistrar接口,重寫其中的registerBeanDefinitions()方法
public class ImportBeanDefinitionRegistrarTest{
@Override
public void registerBeanDefninitions(AnnotationMetadata annotationMetadata,BeanDefinitionRegistry registry{
RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(D.class);
registry.registerBeanDefinition("自定義名稱",rootBeanDefinition);
}
}
//使用上面的類進(jìn)行導(dǎo)入
@Import({ImportBeanDefinitionRegistrarTest})
@Configuration
public class ImportBeanDefinitionRegistrarConfig{
}
三、SpringBoot自動配置原理解析
為了容易分析和理解,我們在IDEA中創(chuàng)建一個SpringBoot項目,創(chuàng)建過程省略,直接跳到該項目的主配置類進(jìn)行分析:
@SpringBootApplication
public class SpringBootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTestApplication.class, args);
}
}
其中@SpringBootApplication注解是SpringBoot項目的重點,按住ctrl鍵進(jìn)入其中,看到它由以下部分組成:
@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 {
//內(nèi)容省略
}
我們主要關(guān)注以下幾個注解:
- @SpringBootConfiguration:標(biāo)記當(dāng)前類為配置類
- @EnableAutoConfiuration:開啟自動配置
- @ComponentScan:掃描主類所在包及其子包、同級包中的Bean
1、@SpringBootConfiguration注解:標(biāo)記當(dāng)前類為配置類
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
根據(jù)其源碼可以知道,@SpringBootConfiguration注解包含@Configuration,所以其擁有@Configuration注解相似的功能,而@Configuration注解又包含@Companent注解,所以配置類也存在于IOC容器中。
2、@EnableAutoConfiguration注解:開啟自動配置
@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 {};
}
根據(jù)其源碼得出其主要由@AutoConfigurationPackages注解和@Import注解組成
@AutoConfigurationPackages注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
//其中的@Import注解導(dǎo)入了Registrar類,該類是AutoConfigurationPackages的靜態(tài)類部類
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
/**
根據(jù)傳入的元注解信息獲取所在的包,將包中組件類封裝為數(shù)組進(jìn)行注冊
*/
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
@Import({AutoConfigurationImportSelector.class})注解
這一步就用到了@Import注解使用方式中的第二中:實現(xiàn)ImportSelector接口
那么AutoConfigurationImportSelector接口何時如何被執(zhí)行呢?
SpringBoot 啟動時會使用 ConfigurationClassParser 來解析被 @Configuration 標(biāo)識的配置類, 然后再處理這個類內(nèi)部被其他注解修飾的情況, 比如 @Import 注解, @ComponentScan 注解,@Bean 注解等
若發(fā)現(xiàn)注解中存在 @Import(ImportSelector) 的情況下,就會創(chuàng)建一個相應(yīng)的 ImportSelector 對象,并調(diào)用其 process 方法
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
});
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
process方法又調(diào)用了getAutoConfigurationEntry方法:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
getAutoConfigurationEntry方法調(diào)用了getCandidateConfigurations方法:
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;
}
getCandidateConfigurations方法中使用了Spring Cor中的類SpringFactoriesLoader,該類的loadFactoryNames方法可以根據(jù)接口獲取接口類的名稱,這個方法返回的是類名的列表,loadFactoryNames方法會遍歷整個springboot項目的classpath下的ClassLoader中所有jar包下的spring.factories文件。至此自動配置結(jié)束
總結(jié)
SpringBoot自動配置是SpringBoot的核心,所以了解SpringBoot的自動配置是非常有必要的,大家可以自行查找資料解釋以下為什么不使用@ComponentScan注解替換@Import注解來進(jìn)行類的導(dǎo)入
以上就是SpringBoot中自動配置原理解析的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot自動配置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring?Security過濾器鏈加載執(zhí)行流程源碼解析
Spring?Boot?對于?Spring?Security?提供了自動化配置方案,可以使用更少的配置來使用?Spring?Security。那么這個過濾器鏈?zhǔn)窃趺醇虞d和實現(xiàn)攔截的呢,對Spring?Security過濾器鏈加載執(zhí)行流程感興趣的朋友一起看看吧2021-12-12
在Spring中利用@Order注解對bean和依賴進(jìn)行排序
在Spring框架中,@Order是一個經(jīng)常被忽視但非常重要的注解,在項目開發(fā)中,當(dāng)我們需要維護(hù)bean的特定順序或者存在許多相同類型的bean時,這個注解就發(fā)揮了作用,這篇文章講的就是如何利用@Order注解對bean和依賴進(jìn)行排序,需要的朋友可以參考下2023-11-11
SpringBoot集成內(nèi)存數(shù)據(jù)庫Sqlite的實踐
sqlite這樣的內(nèi)存數(shù)據(jù)庫,小巧可愛,做小型服務(wù)端演示程序,非常好用,本文主要介紹了SpringBoot集成Sqlite,具有一定的參考價值,感興趣的可以了解一下2021-09-09

