Spring的官方也使用了ImportBeanDefinitionRegistrar的方式,实现了@Component、@Service等注解的动态注入机制
那么如何去使用ImportBeanDefinitionRegistrar这个接口去实现bean的动态注入呢,接下来,让我来为大家揭晓
1. 首先定义一个注解,MyMapper,用来标注需要被动态注入的bean,再定义需要被动态注入的bean TestMapper ,使用该注解标注
package com.mercurius.annotation;
import java.lang.annotation.*;
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface MyMapper {
}
package com.mercurius.bean.test;
import com.mercurius.annotation.MyMapper;
@MyMapper
public class TestMapper {
}
2. 创建MyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar,同时可以继承一些Aware接口,获得spring的一些数据
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanFactoryAware {
private ResourceLoader resourceLoader;
private BeanFactory beanFactory;
//实现了ImportBeanDefinitionRegistrar中的registerBeanDefinitions方法
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
实现registerBeanDefinitions方法。但是有一个问题,我们并不知道需要register哪些bean。这里我们还需要借助一个类ClassPathBeanDefinitionScanner,也就是扫描器,通过扫描器获取我们需要注册的bean。
3. 先简单看一下spring源码中对于ClassPathBeanDefinitionScanner的定义
需要继承ClassPathBeanDefinitionScanner,扫描使用@MyMapper的注解的类
ClassPathBeanDefinitionScanner又继承ClassPathScanningCandidateComponentProvider类,如上图所示
ClassPathScanningCandidateComponentProvider中有两个TypeFilter集合,includeFilters、excludeFilters。满足任意includeFilters会被加载,同样的满足任意excludeFilters不会被加载,如上图所示
扫描类的具体实现
public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
protected void registerFilters() {
addIncludeFilter(new AnnotationTypeFilter(MyMapper.class));
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
}
核心代码就是registerFilters()方法,然后在我们的MyImportBeanDefinitionRegistrar实现类中调用
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry,false);
scanner.setResourceLoader(resourceLoader);
scanner.registerFilters();
scanner.doScan("com.mercurius.bean.test");
}
4. 配置类中使用,导入组件
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class}) //导入组件
public class MainConfig3 {
}
5. 测试效果
public class MainTest4 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
System.out.println("----------------ioc容器加载完成----------------------");
//获取加载的bean的名称
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (int i = 0; i < beanNames.length; i++) {
System.out.println(beanNames[i]);
}
}
}
可以看到我们标注的TestMapper已经被动态注入了