微信公众号:吉姆餐厅ak
学习更多源码知识,欢迎关注。
SpringBoot2 | SpringBoot启动流程源码分析(一)
SpringBoot2 | SpringBoot启动流程源码分析(二)
SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)
SpringBoot2 | SpringBoot Environment源码分析(四)
SpringBoot2 | SpringBoot自定义AutoConfiguration | SpringBoot自定义starter(五)
SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)
SpringBoot2 | 条件注解@ConditionalOnBean原理源码深度解析(七)
SpringBoot2 | Spring AOP 原理源码深度剖析(八)
SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)
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 imports, Set 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 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 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 importBeanDefinitionRegistrars =
new LinkedHashMap<>();
该集合在配置类解析完之后,会单独处理。
ConfigurationClassBeanDefinitionReader
通过loadBeanDefinitions
方法来获取所有的BeanDefinition
,最终会执行以下方法:
上述方法解析并读取配置类中的BeanDefinition
,有一行比较关键:
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
可以看到,该方法传入的参数正是前面存入ConfigurationClass
中的集合对象,也就是ImportBeanDefinitionRegistrar
的所有实现类。
继续来看:
private void loadBeanDefinitionsFromRegistrars(Map 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 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:
https://github.com/admin801122/springboot2-spring5-studying/tree/master/filterChain
本篇主要讲述了 Spring BeanDefinition 注册接口ImportBeanDefinitionRegistrar
的用法,也是一些第三方框架整合 Spring 时的常用扩展接口。
看完本文,你应该能很好的理解@EnableFeignClients
的注册原理了。