上一篇 https://www.cnblogs.com/redwinter/p/16196359.html 介紹了BeanFactoryPostProcessor的執行程序,這篇文章介紹Spring中配置的注解是如何通過ConfigurationClassPostProcessor決議的,另外分析下Spring Boot自動裝配是如何處理的,
ConfigurationClassPostProcessor 決議了哪些注解?
在上一篇文章https://www.cnblogs.com/redwinter/p/16196359.html 我們知道ConfigurationClassPostProcessor實際上是BeanFactoryPostProcessor的一個實作類,他特殊的地方是他還實作了BeanDefinitionRegisterPostProcessor介面,所以ConfigurationClassPostProcessor 既要實作BeanFactoryPostProcessor的介面方法postProcessBeanFactory也要實作BeanDefinitionRegisterPostProcessor的介面方法postProcessBeanDefinitionRegistry,并且在決議的時候先執行了postProcessBeanDefinitionRegistry方法,再執行了postProcessBeanDefinitionRegistry方法,
接下來我們看看postProcessBeanDefinitionRegistry做了什么?
上原始碼:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// 處理配置的BeanDefinition
processConfigBeanDefinitions(registry);
}
整個方法核心是執行了processConfigBeanDefinitions方法,這個方法非常的長并且邏輯也復雜,代碼我就不貼了,說一下大概的流程(較詳細):
- 先進行合格的
beanDefinition的檢查- 獲取到注解的元資料資訊
- 判斷是包含
@Configuration注解,包含則合格,否則判斷是否包含了@Component、@ComponentScan、@Import、@ImportResource注解,包含則合格,如果都不包含則不合格
- 對合格的
BeanDefinition排序 - 創建一個決議
@Configuration注解的決議器 - 對合格的
BeanDefinition集合進行決議- 回圈決議,最終呼叫
processConfigurationClass方法 - 判斷是否跳過決議,比如配置了
@Conditional注解的 - 呼叫
doProcessConfigurationClass方法開始決議(下面的決議中可能會存在遞回呼叫)- 決議
@Component注解- 判斷是否包含內部類標記了
@Component,比如在標有@Component注解的類里面創建一個內部類也標記了@Component注解,如果有就會進行遞回呼叫processConfigurationClass方法
- 判斷是否包含內部類標記了
- 決議
@PropertySources和@PropertySource注解- 比如標記
@PropertySource("classpath:jdbc.properties"),這樣就會把這個屬性的值全部決議到環境資訊的propertySources屬性中
- 比如標記
- 決議
@ComponetScans和@ComponentScan注解- 比如配置了掃描的包,那么就會掃描出合格的
BeanDefinition,然后遞回決議
- 比如配置了掃描的包,那么就會掃描出合格的
- 決議
@Import注解(Spring Boot自動裝配的實作)- 遞回決議出標記了
@Import注解的類放在imports屬性中 - 決議
ImportSelector介面的實作類 - 呼叫
ImportSelector#selectImports方法決議需要注冊的類 - 遞回呼叫
processImports方法,然后將需要注冊的類注冊到importBeanDefinitionRegistrars(這里會在后面進行loadBeanDefinition)
- 遞回決議出標記了
- 決議
@ImportResource注解- 比如決議配置的
Spring的xml組態檔,最終放到importedResources屬性中(后面會進行loadBeanDefinition)
- 比如決議配置的
- 決議
@Bean注解- 比如決議當前類標記了
@Bean的方法 - 然后放在
beanMethods屬性中(后面會進行loadBeanDefinition)
- 比如決議當前類標記了
- 決議
- 加載
BeanDefinition從上面決議出來的類中- 回圈遍歷加載
BeanDefinition - 判斷是否跳過,比如實作了
Condition介面的類 - 加載標有
@Bean的BeanDefinition - 加載從
ImportResource中決議的BeanDefinition - 加載從
ImportSelector中配置的決議的BeanDefinition
- 回圈遍歷加載
- 回圈決議,最終呼叫
整個程序非常復雜,而且存在遞回操作,讀者可以按照我寫的步驟進行debug除錯,當然可能會出現到處跳轉不知所措的情況,多調幾遍就好了,只要知道大致的流程,應該還是不難的,
總的來說就是決議了這些注解:@Component、@PropertySource、@PropertySources、@ComponentScan、@ComponentScans、@Import、@ImportResource、@Bean,然后將標有這些注解的決議成BeanDefinition,如果加上了@Conditionnal注解,那么按照條件進行決議,

自定義自動裝配
現在開發都是用SpringBoot,原因在于他非常的方便,引入即可使用,那么他是做到的呢?眾所周知Spring Boot有幾個注解非常重要,比如:@SpringBootApplication、@EnableAutoConfiguration、@SpringBootConfiguration,其中最重要的是@EnableAutoConfiguration,這個注解里面標記了@Import(AutoConfigurationImportSelector.class),當然還標記了其他的,我們現在只關心這個@Import,里面放入了一個AutoConfigurationImportSelector類,
AutoConfigurationImportSelector類實作了DeferredImportSelector介面,這個DeferredImportSelector介面是ImportSelector的子介面,表示延遲匯入的意思,在上面的分析中,其實最主要的是實作他的介面selectImports,直接原始碼:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 獲取自動裝配的物體
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 獲取合格(候選)的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 加載配置,根據factoryType,這里的FactoryType就是@EnableAutoConfiguration注解
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
// 直接回傳@EnableAutoConfiguration 注解
return EnableAutoConfiguration.class;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 加載spring.factories檔案并決議
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try
// 這里獲取的url就是:
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 讀取屬性檔案,獲取到key為EnableAutoConfiguration,value為需要加載的類
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
所以我們也可以自己寫一個進行自動裝配,接下來實作一個簡單的自動裝配,
定義自動裝配注解
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportSelector.class)
public @interface EnableRedwinterAutoConfiguration {
}
創建MyInportSelector類
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
ClassLoader classLoader = this.getClass().getClassLoader();
// 加載需要裝配的類
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getFactoryTypeClass(), classLoader);
return configurations.toArray(new String[configurations.size()]);
}
private Class<?> getFactoryTypeClass() {
return EnableRedwinterAutoConfiguration.class;
}
}
創建啟動類
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
@Configuration
@EnableRedwinterAutoConfiguration
public class RedwinterApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.redwinter.test.config");
context.refresh();
}
}
創建需要裝配的類
/**
* @author <a href=""https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
@Configuration
public class MyConfiguration {
@Bean
@Conditional(RedwinterStrCondition.class)
public String myStr() {
return "redwinter";
}
public static class RedwinterStrCondition implements ConfigurationCondition {
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println("開始匹配,,,");
return true;
}
}
}
創建spring.factories檔案
com.redwinter.test.config.EnableRedwinterAutoConfiguration=\
com.redwinter.test.config.MyConfiguration
啟動驗證
debug斷點:

這就是Spring Boot自動裝配的簡化版,總得來說我們完成了Spring 對BeanFactoryPostProcessor的執行程序的決議,包括Spring是如何進行注解決議的,其實就是Spring在對BeanDefinition在正式初始化為Bean的前置處理,所以我們可以這個階段進行很多擴展,比如占位符的處理PropertySourcesPlaceholderConfigurer等,
接下來接續解讀AbstractApplicationContext#refresh方法對BeanPostProcessor的注冊,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/465951.html
標籤:Java
