在阅读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