在阅读 Spring Boot 源码时, 看到 Spring Boot 中大量使用 ImportBeanDefinitionRegistrar 来实现 Bean 的动态注入. 它是 Spring 中一个强大的扩展接口. 本篇文章来讲讲它相关使用.
Spring Boot 中的使用
在 Spring Boot 内置容器的相关自动配置中有一个 ServletwebServerFactoryAutoConfiguration 类. 该类的部分代码如下:
- @Configuration(proxyBeanMethods = false)
- @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
- @ConditionalOnClass(ServletRequest.class)
- @ConditionalOnWebApplication(type = Type.SERVLET)
- @EnableConfigurationProperties(ServerProperties.class)
- @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
- ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
- ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
- ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
- public class ServletWebServerFactoryAutoConfiguration {
- // ...
- /**
- * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
- * {@link ImportBeanDefinitionRegistrar} for early registration.
- */
- public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
- private ConfigurableListableBeanFactory beanFactory;
- // 实现 BeanFactoryAware 的方法, 设置 BeanFactory
- @Override
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
- if (beanFactory instanceof ConfigurableListableBeanFactory) {
- this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
- }
- }
- // 注册一个 WebServerFactoryCustomizerBeanPostProcessor
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
- BeanDefinitionRegistry registry) {
- if (this.beanFactory == null) {
- return;
- }
- registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
- WebServerFactoryCustomizerBeanPostProcessor.class);
- registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
- ErrorPageRegistrarBeanPostProcessor.class);
- }
- // 检查并注册 Bean
- private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
- // 检查指定类型的 Bean name 数组是否存在, 如果不存在则创建 Bean 并注入到容器中
- if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
- RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
- beanDefinition.setSynthetic(true);
- registry.registerBeanDefinition(name, beanDefinition);
- }
- }
- }
- }
在这个自动配置类中, 基本上展示了 ImportBeanDefinitionRegistrar 最核心的用法. 这里该接口主要用来注册 BeanDefinition.
BeanPostProcessorsRegistrar 实现了 ImportBeanDefinitionRegistrar 接口和 BeanFactoryAware 接口. 其中 BeanFactoryAware 接口的实现是用来暴露 Spring 的 ConfigurableListableBeanFactory 对象.
而实现 registerBeanDefinitions 方法则是用来对 Bean 的动态注入, 这里注入了 WebServerFactoryCustomizerBeanPostProcessor 和 ErrorPageRegistrarBeanPostProcessor.
简单了解了 Spring Boot 中的一个使用实例, 下面我们总结一下使用方法, 并自己实现一个类似的功能.
ImportBeanDefinitionRegistrar 使用
Spring 官方通过 ImportBeanDefinitionRegistrar 实现了 @Component,@Service 等注解的动态注入机制.
很多三方框架集成 Spring 的时候, 都会通过该接口, 实现扫描指定的类, 然后注册到 spring 容器中. 比如 Mybatis 中的 Mapper 接口, springCloud 中的 FeignClient 接口, 都是通过该接口实现的自定义注册逻辑.
所有实现了该接口的类的都会被 ConfigurationClassPostProcessor 处理, ConfigurationClassPostProcessor 实现了 BeanFactoryPostProcessor 接口, 所以 ImportBeanDefinitionRegistrar 中动态注册的 bean 是优先于依赖其的 bean 初始化, 也能被 aop,validator 等机制处理.
基本步骤:
实现 ImportBeanDefinitionRegistrar 接口;
通过 registerBeanDefinitions 实现具体的类初始化;
在 @Configuration 注解的配置类上使用 @Import 导入实现类;
简单示例
这里实现一个非常简单的操作, 自定义一个 @Mapper 注解 (并非 Mybatis 中的 Mapper 实现), 实现类似 @Component 的功能, 添加了 @Mapper 注解的类会被自动加载到 spring 容器中.
首先创建 @Mapper 注解.
- @Documented
- @Inherited
- @Retention(RetentionPolicy.RUNTIME)
- @Target({
- ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER
- })
- public @interface Mapper {
- }
创建 UserMapper 类, 用于使用 @Mapper 注.
- @Mapper
- public class UserMapper {
- }
定义 ImportBeanDefinitionRegistrar 的实现类 MapperAutoConfigureRegistrar. 如果需要获取 Spring 中的一些数据, 可实现一些 Aware 接口, 这实现了 ResourceLoaderAware.
- public class MapperAutoConfigureRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
- private ResourceLoader resourceLoader;
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- MapperBeanDefinitionScanner scanner = new MapperBeanDefinitionScanner(registry, false);
- scanner.setResourceLoader(resourceLoader);
- scanner.registerFilters();
- scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
- scanner.doScan("com.secbro2.learn.mapper");
- }
- @Override
- public void setResourceLoader(ResourceLoader resourceLoader) {
- this.resourceLoader = resourceLoader;
- }
- }
在上面代码中, 通过 ResourceLoaderAware 接口的 setResourceLoader 方法获得到了 ResourceLoader 对象.
在 registerBeanDefinitions 方法中, 借助 ClassPathBeanDefinitionScanner 类的实现类来扫描获取需要注册的 Bean.
MapperBeanDefinitionScanner 的实现如下:
- public class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
- public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
- super(registry, useDefaultFilters);
- }
- protected void registerFilters() {
- addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
- }
- @Override
- protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
- return super.doScan(basePackages);
- }
- }
MapperBeanDefinitionScanner 继承子 ClassPathBeanDefinitionScanner, 扫描被 @Mapper 的注解的类.
在 MapperBeanDefinitionScanner 中指定了 addIncludeFilter 方法的参数为包含 Mapper 的 AnnotationTypeFilter.
当然也可以通过 excludeFilters 指定不加载的类型. 这两个方法由它们的父类 ClassPathScanningCandidateComponentProvider 提供的.
完成了上面的定义, 则进行最后一步引入操作了. 创建一个自动配置类 MapperAutoConfig, 并通过 @Import 引入自定义的 Registrar.
- @Configuration
- @Import(MapperAutoConfigureRegistrar.class)
- public class MapperAutoConfig {
- }
至此, 整个代码的功能已经编写完成, 下面写一个单元测试.
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class MapperAutoConfigureRegistrarTest {
- @Autowired
- UserMapper userMapper;
- @Test
- public void contextLoads() {
- System.out.println(userMapper.getClass());
- }
- }
执行单元测试代码, 会发现打印如下日志:
class com.secbro2.learn.mapper.UserMapper
说明 UserMapper 已经被实例化成功, 并注入 Spring 容器当中.
小结
当然, 这里的 UserMapper 并不接口, 这里的实现也并不是 Mybatis 中的实现形式. 只是为了演示该功能的简单示例. 需要注意的是文中提到了两种实现的实例, 第一种是 Spring Boot 中的实现, 第二种是我们的 Mapper 实例. 展现了两种不同方法的注册的操作, 但整个使用流程是一致的, 读者注意仔细品味, 并在此基础上进行拓展更复杂的功能.
来源: https://www.cnblogs.com/secbro/p/11991641.html