1. @SpringBootApplication
1.1 @SpringBootApplication註解
萬事開頭難,一旦開始就不要停,不要停,不要,直到她說停。
一切從 @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 {
}
@Inherited註解聲明註解上時,在類上表明該類具有繼承性。子類會自動繼承該註解。
需要我們注重關注三個註解:@SpringBootConfiguration、@ComponentScan、@EnableAutoConfiguration
1.1.1 @SpringBootConfiguration
1.1.2 @ComponentScan
1.1.3 @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 {};
}
在 @EnableAutoConfiguration 註解上使用了 @Import註解導入了一個配置類AutoConfigurationImportSelector,看到這裏如果看過前一篇
SpringBoot自動裝配原理分析之上篇我想你應該知道AutoConfigurationImportSelector必然是ImportSelector或ImportBeanDefinitionRegistrar的實現類。
2 AutoConfigurationImportSelector
我們可以看到一些信息:
AutoConfigurationImportSelector
它實現了DeferredImportSelector
接口,而DeferredImportSelector
接口繼承自ImportSelector
接口;- 實現了
Ordered
接口定義加載順序的接口;
//從定義上可以看到實現了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//....略
}
2.1 解讀方法#selectImports
小夥伴兒,我們回到AutoConfigurationImportSelector
類中,又要準備深入了,雞凍不…哈哈
看看方法名,selectImports
選擇導入,導入什麼,顯然就是要自動裝配的類。怎麼還要選擇性的導入自動裝配呢?我們想想是不是要過濾一些,排除一些多餘的呢?這個猜想不錯,我們看看就明白究竟了。
//入參是註解屬性元信息
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
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.filter(configurations, autoConfigurationMetadata);
//自動裝配事件
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
小夥伴兒們,又需要我們逐一層層深入了。記住,只有深入了,你纔會體驗到前所未有的爽,那感覺終生難忘,回味無窮,嘿嘿,,,,看源碼是痛苦的,但看明白後就有種醍醐灌頂,豁然開朗的感覺。
2.2 獲取候選裝配組件
解讀方法getCandidateConfigurations
從命名上可以知道Candidate是候選的意思,大概就是獲取候選配置類信息
。
public class AutoConfigurationImportSelector{
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//加載在 `META-INF/spring.factories` 裏的類名的數組,他們作爲自動裝配的候選類名單
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;
}
}
}
我們繼續跟進SpringFactoriesLoader.loadFactoryNames
這個方法看看
public final class SpringFactoriesLoader {
//配置文件路徑
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
//此處又調用了該類的私有方法#loadSpringFactories()
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//小夥伴兒們我們繼續深入....有多深都要進去,只是進去看看
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//MultiValueMap就是個Map,因爲返回就是Map
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
//加載指定路徑下文件內容
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
//轉爲Resource
UrlResource resource = new UrlResource(url);
//加載爲Peroperties文件進行讀取
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
//我們打開META-INF/spring.factories文件看看,
//可以發現文件key是接口的全類名,value是實現類的全類名列表,代碼自然就懂了
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
}
說明:SpringFactoriesLoader
是Spring的工廠機制加載器,看完#loadSpringFactories方法,明白原理如下:
- 加載指定ClassLoader下META-INF/spring.factories文件資源內容
- 轉爲Properties文件進行讀取,放入Map中,key爲接口的全類名,value爲實現類的全類名列表,然後進行返回。
找到maven依賴的自動裝配模塊org.springframework.boot:spring-boot-autoconfigure,META-INF/spring.factories文件下,以EnableAutoConfiguration
全類名爲Key的配置如下所示:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
//其他略.......
2.3 排除自動裝配組件
接下來,執行this.getExclusions(annotationMetadata, attributes) 方法,去排除一些不需要的組件。
這樣說你可能還不清楚,比如在@EnableAutoConfiguration
註解定義中可以看到
public @interface SpringBootApplication{
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
有兩個屬性分別是exclude
和excludeName
,在實際項目中你可能也這樣寫過,配置多數據源時,需要我們排除默認的數據源,使用我們自己配置的數據源,配置如下:
@SpringBootApplication(exclude={
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class})
這就是要告訴自動裝配需要排除的組件類了。
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet();
//@EnableAutoConfiguration註解中exclude和excludeName用來添加排除的組件
excluded.addAll(this.asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(this.getExcludeAutoConfigurationsProperty());
return excluded;
}
將需要排除的類放在集合中,然後返回,之後檢查排除的集合類是否合法最後進行移除。
2.4 過濾自動裝配組件
繼續往下看了,調用configurations = this.filter(configurations, autoConfigurationMetadata); 方法進行過濾。
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
//主要方法getAutoConfigurationImportFilters()
Iterator var8 = this.getAutoConfigurationImportFilters().iterator();
while(var8.hasNext()) {
//可以看出迭代的類是AutoConfigurationImportFilter的子類!!!!
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
this.invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
//爲節省篇幅,其他代碼省略....
}
}
//#filter()方法調用
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
返回進入SpringFactoriesLoader
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
return result;
}
可以看出AutoConfigurationImportSelector#filter中的AutoConfigurationImportFilter 對象集合是有SpringFactoriesLoader 加載的。找到maven依賴的自動裝配模塊org.springframework.boot:spring-boot-autoconfigure,META-INF/spring.factories文件下
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
我們可以看到,SpringFactoriesLoader#loadFactories方法與我們前面看獲取候選自動裝配方法時調用的SpringFactoriesLoader#loadFactoryNames 差別是後者是靜態方法,前者是調用後者的。點開OnClassCondition 可以發現它是AutoConfigurationImportFilter實現類。可以說明AutoConfigurationImportSelector#filter方法的作用是過濾META-INF/spring.factories 資源中那些當前ClassLoader不存在的類。
SpringBoot內部提供了特有的註解:條件註解(Conditional Annotation)。比如@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnExpression、@ConditionalOnMissingBean等。@ConditionalOnClass用來判斷需要依賴自動裝配的class必需被ClassLoader提前裝載,然後解析其註解元信息,從而跟據依賴是否存在來判斷是否裝載。
2.4 自動裝配事件
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
Iterator var5 = listeners.iterator();
while(var5.hasNext()) {
AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
this.invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
AutoConfigurationImportListener用來監聽AutoConfigurationImportEvent