SpringBoot自動配置原理
SpringBoot自動配置原理(SpringBoot自動裝配原理,SpringBoot starter原理)
SpringBoot可以根據(jù)定義在classpath下的類滨砍,自動的給你生成一些Bean妓肢,并加載到Spring的Context中街州,自動配置充分的利用了Spring 4.0的條件化配置特性,能夠自動配置特定的Spring bean,用來啟動某項特性告希;
關(guān)于條件化@Conditional注解:
如果你希望一個bean在某些條件下加載桥温,在某些條件下不加載引矩,則可以使用@Conditional注解;
@Configuration
public class MyConfig {
@Bean
@Conditional(MyBeanCondition.class)
public MyBean myBean(){
return new MyBean();
}
}
public class MyBeanCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
當(dāng)@Conditional(MyBeanCondition.class)為true時侵浸,MyBean才會被創(chuàng)建旺韭,否則不會創(chuàng)建;
有了條件注解以保證某些bean在沒滿足特定條件的情況下就可以不必初始化霹娄,避免在bean初始化過程中由于條件不足渠啊,導(dǎo)致應(yīng)用啟動失敗口叙。
Conditional:有條件的; 條件; 有條件; 條件響應(yīng); 條件式;
01织盼、ConditionalOnBean 當(dāng)spring容器中有某個bean時
02、ConditionalOnClass 當(dāng)有某個class時
03酱塔、ConditionalOnMissingBean 當(dāng)沒有某個bean時
04沥邻、ConditionalOnMissingClass
05、ConditionalOnCloudPlatform
06延旧、ConditionalOnExpression
07谋国、ConditionalOnJava
08、ConditionalOnJndi
09迁沫、ConditionalOnNotWebApplication
10芦瘾、ConditionalOnProperty
11、ConditionalOnResource
12集畅、ConditionalOnSingleCandidate
13近弟、ConditionalOnWebApplication
14、ConditionalOnRepositoryType
15挺智、ConditionalOnMissingFilterBean
16祷愉、ConditionalOnEnabledResourceChain
在編寫SpringBoot項目時,入口類上都會使用@SpringBootApplication注解了赦颇,我們可以看一下源代碼
@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 注解的變量
//exclude : 排除指定的類
//@SpringBootApplication(exclude = RedisAutoConfiguration.class)可以排除自定義配置等等二鳄,根據(jù)自己的需要進(jìn)行定制。
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
//excludeName:排除指定的類名媒怯,通過bean name來進(jìn)行排除指定的類订讼,如下:
//@SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration")
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
//scanBasePackages: 掃描指定的包添加到Spring容器中,參數(shù)為數(shù)組類型:
//@SpringBootApplication(scanBasePackages="com.bjpowernode.boot.component")
//掃描的包能注冊識別扇苞,沒有掃描的包將不能注冊識別欺殿;
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
//scanBasePackageClasses: 掃描指定包的類并添加到Spring容器中寄纵,參數(shù)為Class類型數(shù)組格式:
//@SpringBootApplication(scanBasePackageClasses=MyComponent.class)
//注冊的類能識別,在同級包下或子包下的都能注冊脖苏,否則不能識別
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
boolean proxyBeanMethods() default true;
}
@SpringBootApplication注解本身又是一個復(fù)合注解程拭,它等效于如下四個注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@ConfigurationPropertiesScan
@SpringBootConfiguration
該注解等效于@Configuration,那也就是說這個注解相當(dāng)于@Configuration棍潘,所以這兩個注解作用是一樣的恃鞋,它是讓我們能夠去注冊一些額外的Bean,或者導(dǎo)入一些額外的配置蜒谤,@Configuration還表示該類是一個配置類山宾,不需要額外的xml進(jìn)行配置,同時該類也是Spring容器中的一個bean鳍徽。
@EnableAutoConfiguration (重點)
該注解是Spring Boot自動配置注解资锰,Spring Boot中的自動配置主要是@EnableAutoConfiguration的功勞,該注解可以讓Spring Boot根據(jù)類路徑中的jar包依賴為當(dāng)前項目進(jìn)行自動配置.
@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 {};
}
該注解由如下兩個注解構(gòu)成:
1.@AutoConfigurationPackage
讓包中的類以及子包中的類能夠被自動掃描到Spring容器中阶祭;
2.@Import(EnableAutoConfigurationImportSelector.class)
這個注解就是通過import的方式將EnableAutoConfigurationImportSelector添加到Spring容器中绷杜;
EnableAutoConfigurationImportSelector 實現(xiàn)自動化配置導(dǎo)入
Spring框架本身也提供了幾個名字為@Enable開頭的Annotation定義。比如@EnableScheduling濒募、@EnableCaching等鞭盟,@EnableAutoConfiguration的理念和這些注解其實是一脈相承的。
@EnableScheduling是通過@Import將Spring調(diào)度框架相關(guān)的bean定義都加載到IoC容器瑰剃。
@EnableAutoConfiguration也是借助@Import的幫助齿诉,將所有符合自動配置條件的bean定義加載到IoC容器;
該注解通過@Import注解導(dǎo)入了一個組件:AutoConfigurationImportSelector晌姚,該組件實現(xiàn)了接口:
public interface DeferredImportSelector extends ImportSelector
該接口主要是為了導(dǎo)入@Configuration的配置項粤剧,而DeferredImportSelector是延期導(dǎo)入,當(dāng)所有的@Configuration都處理過后才會執(zhí)行挥唠;
導(dǎo)入AutoConfigurationImportSelector類,會執(zhí)行到它的process方法,如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...............................................................................
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//getAutoConfigurationMetadata() 獲取自動化配置的元數(shù)據(jù)
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
//獲取自動化配置元數(shù)據(jù)
//AutoConfigurationMetadataLoader.loadMetadata 通過JDK Properties.load(inputStream) 來加載META-INF/spring-autoconfigure-metadata.properties中配置的需要自動加載的類,并最后封裝成PropertiesAutoConfigurationMetadata對象,將加載好的properties信息賦值其properties屬性
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
}
}
META-INF/spring-autoconfigure-metadata.properties中配置項,如下:
它的意思同@ConditionalOnClass注解,key為權(quán)限類名, value為key所依賴的其他Class,如果value不存在,則key所代表的類不會自動裝配
接下來((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//獲取注解@EnableAutoConfiguration的屬性,即exclude,excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//加載文件中配置的自動化配置META-INF/spring.factories
// loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); 中g(shù)etOrDefault 取出org.springframework.boot.autoconfigure.EnableAutoConfiguration為key的value
//Enumeration<URL> urls =classLoader.getResources("META-INF/spring.factories")
//URL url = urls.nextElement();
//UrlResource resource = new UrlResource(url);
//InputStream is = resource.getInputStream();
//Properties.load(is)
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = removeDuplicates(configurations);
//獲取注解上的排除掉的類
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//刪除掉排除 項
configurations.removeAll(exclusions);
//過濾,過濾掉那些,還不滿足加載條件的類,即類的@Conditional的條件沒滿足,則不加載,去除掉
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
configurations = filter(configurations, autoConfigurationMetadata);的大概邏輯如下:
META-INF/spring.factories中配置的自動配置類(org.springframework.boot.autoconfigure.EnableAutoConfiguration的 value類),這些類會被作為自動裝配的候選類,接下來會根據(jù)META-INF/spring-autoconfigure-metadata.properties這個中配置的類與依賴類的對應(yīng)關(guān)系,去校驗該類所依賴的Class(ConditionalOnClass)是否存在(拿到className,通過Class.forName方法,不拋異常就說明該class存在),如果存在,則該類可以被自動裝配.
另外,這個地放比較耗時間,springboot將數(shù)據(jù)一分為二,并另起了一個線程,與主線程一起執(zhí)行校驗,如下代碼所示之處.
private ThreadedOutcomesResolver(OutcomesResolver outcomesResolver) {
this.thread = new Thread(() -> this.outcomes = outcomesResolver.resolveOutcomes());
this.thread.start();
}
@EnableAutoConfiguration與@Conditional
@EnableAutoConfiguration自動加載配置抵恋,@Conditional根據(jù)環(huán)境決定是否解析處理配置,這兩個注解的配合完成了自動化配置功能宝磨;
在Spring Boot中怎么自定義自動配置弧关?
1、需要提供jar包唤锉,在jar包中需要包含META-INF/spring.factories文件世囊;
2、在spring.factories中添加配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration =
com.bjpowernode.xxxx.xxxConfiguration(該類我們自己去實現(xiàn)窿祥,里面主要是要做自動化的配置茸习,給使用者把該配的配置好,讓別人可以直接用)
3壁肋、xxxConfiguration的實現(xiàn)需要添加注解@Configuration号胚;
4、xxxConfiguration也可以選擇添加@Conditional來適應(yīng)不同的環(huán)境浸遗;
5猫胁、在xxxConfiguration類中實現(xiàn)自動化配置;
有了SpringBoot的自動化配置跛锌,我們可以靈活的自定義我們自己的自動配置弃秆,當(dāng)應(yīng)用需要該功能時,只需要簡單的依賴該jar包即可髓帽,同時Spring Boot為我們提供的條件注解菠赚,同樣的代碼可以靈活適應(yīng)各種環(huán)境;