SpringBoot BeanDefinition注册核心类ImportBeanDefinitionRegistrar源码分析
概述
本篇来介绍一个Spring强大的扩展接口:ImportBeanDefinitionRegistrar
,该接口主要用来注册beanDefinition
。很多三方框架集成Spring 的时候,都会通过该接口,实现扫描指定的类,然后注册到spring 容器中。
比如 Mybatis 中的Mapper接口,springCloud中的 FeignClient 接口,都是通过该接口实现的自定义注册逻辑。
Mybatis中的扫描实现类如下:
源码分析
大致分两个步骤来介绍:
- 注册所有的
ImportBeanDefinitionRegistrar
实现类 - 执行所有的
ImportBeanDefinitionRegistrar
的逻辑 - 自定义扫描注册
BeanDefinition
组件
分别来看。
注册所有的 ImportBeanDefinitionRegistrar 实现类
在 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<>();
该集合在配置类解析完之后,会单独处理。
执行所有的 ImportBeanDefinitionRegistrar 的逻辑
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 时的常用扩展接口。
链接:https://juejin.im/post/5ce4d92551882532b930124a
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:https://choupangxia.com/2019/12/05/springboot-beandefinition/