在看源码的时候,我们发现了一个接口ImportBeanDefinitionRegistrar,当时只是简单的了解了一下spring的大概,没有做复习。在我们开始学习mybatis源码的时候,发现这个接口成为我们必要学习的东西。那么这个接口是做什么的,还有就是这个接口的原理是什么。怀着疑问,让我们学习一下吧!
首先我们还是不去直接看原理,我们还是先根据网上资料实践一下这个接口的功能。根据资料,该接口的主要作用就是用来动态的注入bean到spring ioc中。但是问题是什么是动态,或者怎么样的操作才算是动态。难道以前都不算动态吗。作者对这里的动态的理解是可以用户自定义的决定注入哪些类。这里的自定义就是规则。根据作者的实践,认为就是注入作者自定义的注解的bean到ioc容器中。那么如何实现一个自定义的动态注入器。这里我们就实践一下。
1.自定义定义注解(相当于打个标志,让spring扫描的时候进行识别)
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface TianMapper {
}
2.定义要注入的类(该类被自定义注解修饰
@TianMapper
@Data
public class TianConfig {
public String toString(){
return this.getClass()+"TianConfig";
}
})
3.自定义扫描器(自定义扫描的包和限制被扫描的类的标志等)
@Data
public class TianClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private Class annotationClass;
public TianClassPathMapperScanner(BeanDefinitionRegistry registry) {
super(registry);
}
}
4.实现ImportBeanDefinitionRegistrar接口,在spring启动时候扫描自定义注解的类
public class TianImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanFactoryAware {
private ResourceLoader resourceLoader;
private BeanFactory beanFactory;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
TianClassPathMapperScanner tianClassPathMapperScanner=new TianClassPathMapperScanner(registry);
//设置基本的注解
tianClassPathMapperScanner.setAnnotationClass(TianMapper.class);
//设置运行被扫描的注解
tianClassPathMapperScanner.addIncludeFilter(new AnnotationTypeFilter(TianMapper.class));
//设置需要进行扫描的包
tianClassPathMapperScanner.scan("com.scaffold.simple.admin.springtest");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
5.将需要被spring启动执行的接口加入到ioc中。
@Import(TianImportBeanDefinitionRegistrar.class)
运行源代码,发现我们的TianConfig已经注入进去了。
通过上述的学习,我们就大概知道该接口就是对spring类扫描规范的一种扩展,可以让我们开发人员自定义注解然后交由ioc容器进行管理。其实这种用法在一些第三方包中应用的非常广泛。比如开始的时候说的mybatis。
二,实现的原理
作者通过代码跟踪,最后发现最终调用的访问如下:
在invokeBeanFactoryPostProcessors(beanFactory);方法中,
最终的调用实现是for循环实现。
private void loadBeanDefinitionsFromRegistrars(Mapregistrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
总结:spring在invokeBeanFactoryPostProcessors方法对类进行类级别注解解析扫描的时候,顺便就将ImportBeanDefinitionRegistrar接口的实现类标记出来。并进行全部解析然后转化成初级的beandefine,并在最后的onfresh过程中真正的实例化。