ImportBeanDefinitionRegistrar动态注册Bean

一、简介

  • ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类。
  • 使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean。
  • 实现该接口的类拥有注册bean的能力。

二、手动把一个类注册成bean

1、首先写一个类,最终要把它注册为bean。

public class HelloService {

}

2、自定义ImportBeanDefinitionRegistrar实现类手动注册bean。

public class HelloImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

/**
 * @Description AnnotationMetadata:当前类的注解信息;
 * BeanDefinitionRegistry:注册类,其registerBeanDefinition()可以注册bean
 **/@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//扫描注解
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
String[] basePackages = (String[]) annotationAttributes.get("basePackages");

//扫描类
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);

scanner.addIncludeFilter(helloServiceFilter);
scanner.scan(basePackages);
}
}

3、最后定义一个配置类发现一下上面的ImportBeanDefinitionRegistrar实现类。

@Configuration
@ComponentScan("com.ntan520.test")
@Import(HelloImportBeanDefinitionRegistrar.class)
public class HelloConfiguration {

}

4、测试

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = {HelloConfiguration.class}) //表示只需要这一个文件
public class DemoApplicationTest {

@Resource
HelloService helloService;

/**
* @Description 扫描到helloService
**/@Test
public void contextLoads() {
  System.out.println(helloService.getClass());
}
}

三、自定义注解实现@Component的功能

1、需求

自定义一个注解@Mapper,配合其他类,实现被注解类可以被注册成bean的功能。

2、@Mapper注解定义

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {

}

3、注解使用

注释于类上:

@Mapper
public class TestMapper {
}

4、注册类定义

实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法,手动注册bean;同时实现一些Aware接口,以便获取Spring的一些数据。

public class MapperAutoConfiguredMyBatisRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanFactoryAware {
      
private ResourceLoader resourceLoader;
private BeanFactory beanFactory;

/**
 * @Description 注册bean,但不知道需要注册哪些bean,所以需要借助
 * ClassPathBeanDefinitionScanner扫描器,扫描需要注册的bean
 **/@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
MyClasssPathBeanDefinitionScanner scanner = new MyClasssPathBeanDefinitionScanner(registry,false);
scanner.setResourceLoader(resourceLoader);
scanner.registerFilters();
scanner.doScan("com.ntan520.test");
}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory=beanFactory;
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}

5、扫描器定义

定义扫描器ClassPathBeanDefinitionScanner,通过它来获取需要注册的bean。

public class MyClasssPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

public MyClasssPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}

/**
 * @Description 注册条件过滤器,将@Mapper注解加入允许扫描的过滤器中,
 * 如果加入排除扫描的过滤器集合excludeFilter中,则不会扫描
 **/protected void registerFilters() {
addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
}

@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
}

6、测试

/**
 * 只将MapperAutoConfig类纳入测试环境的Spring容器中,
 * 或@ContextConfiguration(classes = {MapperAutoConfig.class})
 */@RunWith(SpringRunner.class)
@SpringBootTest(classes = MapperAutoConfig.class)
public class DemoApplicationTest {

@Resource
TestMapper testMapper;

@Test
public void contextLoads() {
System.out.println(testMapper.getClass());
}
}

发表评论