微信公衆號:吉姆餐廳ak 學習更多源碼知識,歡迎關注。 java
![]()
SpringBoot2 | SpringBoot啓動流程源碼分析(一)git
SpringBoot2 | SpringBoot啓動流程源碼分析(二)github
SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)spring
SpringBoot2 | SpringBoot Environment源碼分析(四)springboot
SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)bash
SpringBoot2 | SpringBoot監聽器源碼分析 | 自定義ApplicationListener(六)微信
SpringBoot2 | 條件註解@ConditionalOnBean原理源碼深度解析(七)app
SpringBoot2 | Spring AOP 原理源碼深度剖析(八)框架
SpringBoot2 | SpingBoot FilterRegistrationBean 註冊組件 | FilterChain 責任鏈源碼分析(九)ide
SpringBoot2 | BeanDefinition 註冊核心類 ImportBeanDefinitionRegistrar (十)
SpringBoot2 | Spring 核心擴展接口 | 核心擴展方法總結(十一)
本篇來介紹一個Spring強大的擴展接口:ImportBeanDefinitionRegistrar
,該接口主要用來註冊beanDefinition
。不少三方框架集成Spring 的時候,都會經過該接口,實現掃描指定的類,而後註冊到spring 容器中。 好比 Mybatis 中的Mapper接口,springCloud中的 FeignClient 接口,都是經過該接口實現的自定義註冊邏輯。
Mybatis中的掃描實現類以下:
大體分兩個步驟來介紹:
ImportBeanDefinitionRegistrar
實現類ImportBeanDefinitionRegistrar
的邏輯BeanDefinition
組件分別來看。
在 spring 容器啓動加載配置類階段,會執行配置類註解@Import
的邏輯: 對應邏輯在ConfigurationClassParser
中:
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
//收集全部的導入配置類
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
複製代碼
上述方法利用了遞歸解析,直至獲取全部的導入類。
來看解析導入類邏輯:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//類型判斷是否爲 ImportSelector類型
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
//這裏進行類型判斷是否爲 ImportBeanDefinitionRegistrar 類型
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
//直接實例化
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//遞歸解析
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
複製代碼
上述邏輯對@Import
導入類進行判斷,若是爲ImportBeanDefinitionRegistrar
類型,則直接實例化,並加入到ConfigurationClass
集合中:
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
複製代碼
該集合在配置類解析完以後,會單獨處理。
ConfigurationClassBeanDefinitionReader
經過loadBeanDefinitions
方法來獲取全部的BeanDefinition
,最終會執行如下方法:
上述方法解析並讀取配置類中的BeanDefinition
,有一行比較關鍵:
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
複製代碼
能夠看到,該方法傳入的參數正是前面存入ConfigurationClass
中的集合對象,也就是ImportBeanDefinitionRegistrar
的全部實現類。 繼續來看:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
複製代碼
遍歷全部的實現類,執行註冊邏輯。 Mybatis,Feignclient大體作法:經過實現了該接口,而後註冊各自指定的類或接口:mapper接口或者feignClient接口,而後將接口聲明爲 FactoryBean
,設置攔截方法,生成代理類。
BeanDefinition
組件自定義註解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyAutoBeanDefinitionRegistrar3.class)
public @interface EnableMyAutoRegistrar3 {
}
複製代碼
自定義註冊實現類:
public class MyAutoBeanDefinitionRegistrar3 implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scan = getScanner();
//指定註解,相似於Feign註解
scan.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class));
Set<BeanDefinition> candidateComponents = scan.findCandidateComponents("com.beanDefinition.registrar.component");
BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
candidateComponents.stream().forEach(beanDefinition -> {
String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
if (!registry.containsBeanDefinition(beanDefinition.getBeanClassName())) {
registry.registerBeanDefinition(beanName, beanDefinition);
}
});
}
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false) {
// FeignClient 重寫了 ClassPathScanningCandidateComponentProvider 匹配邏輯
@Override
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
if (beanDefinition.getMetadata().isIndependent()) {
// TODO until SPR-11711 will be resolved
// 判斷接口是否繼承了 Annotation註解
if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata()
.getInterfaceNames().length == 1 && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
try {
Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(),
MyAutoBeanDefinitionRegistrar3.this.classLoader);
return !target.isAnnotation();
} catch (Exception ex) {
this.logger.error(
"Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);
}
}
return true;
}
return false;
}
};
}
}
複製代碼
具體代碼 Github: github.com/admin801122…
本篇主要講述了 Spring BeanDefinition 註冊接口ImportBeanDefinitionRegistrar
的用法,也是一些第三方框架整合 Spring 時的經常使用擴展接口。
看完本文,你應該能很好的理解@EnableFeignClients
的註冊原理了。