ImportBeanDefinitionRegistrar 定义:

一个类实现 ImportBeanDefinitionRegistrar ,通过 configuration 注解和 import 注解可以动态注入一个bean 到spring 容器。mybatis mapper 类的扫码注册就是通过 ImportBeanDefinitionRegistrar 实现的。

spring 框架自动配置,与其他框架组件融合时,都是通过 ImportBeanDefinitionRegistrar 实现的。比如 feign的自动配置,MybatisAutoConfiguratio、以及很多以 Enable 开头的配置类。

  1. package org.springframework.context.annotation;
  2. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  3. import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
  4. import org.springframework.core.type.AnnotationMetadata;
  5. /**
  6. * Interface to be implemented by types that register additional bean definitions when
  7. * processing @{@link Configuration} classes. Useful when operating at the bean definition
  8. * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
  9. *
  10. * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
  11. * may be provided to the @{@link Import} annotation (or may also be returned from an
  12. * {@code ImportSelector}).
  13. *
  14. * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
  15. * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
  16. * methods will be called prior to {@link #registerBeanDefinitions}:
  17. * <ul>
  18. * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
  19. * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
  20. * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
  21. * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
  22. * </ul>
  23. *
  24. * <p>See implementations and associated unit tests for usage examples.
  25. *
  26. * @author Chris Beams
  27. * @since 3.1
  28. * @see Import
  29. * @see ImportSelector
  30. * @see Configuration
  31. */
  32. public interface ImportBeanDefinitionRegistrar {
  33. /**
  34. * Register bean definitions as necessary based on the given annotation metadata of
  35. * the importing {@code @Configuration} class.
  36. * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
  37. * registered here, due to lifecycle constraints related to {@code @Configuration}
  38. * class processing.
  39. * @param importingClassMetadata annotation metadata of the importing class
  40. * @param registry current bean definition registry
  41. */
  42. public void registerBeanDefinitions(
  43. AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
  44. }

使用方法

1.一个类实现ImportBeanDefinitionRegistrar 接口;
2.registerBeanDefinitions 实现bean 的注册逻辑;
3.在 @configuration 注解的类上面使用 import 注解导入实现 ImportBeanDefinitionRegistrar 接口的类;

Demo

自动扫码注册包含 Mapper 注解的类到spring 容器

1.自定义注解

  1. /**
  2. * @author: michael
  3. * @create: 2020/12/30
  4. */
  5. @Inherited
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Mapper {
  9. }

2.定义一个类实现 ImportBeanDefinitionRegistrar 接口

通过重写registerBeanDefinitions 方法,把包含 mapper 注解的类扫描注册到 spring 容器。

  1. public class MapperDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  2. private ResourceLoader resourceLoader;
  3. @Override
  4. public void setResourceLoader(ResourceLoader resourceLoader) {
  5. this.resourceLoader = resourceLoader;
  6. }
  7. @Override
  8. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  9. MapperDefinitionScanner scanner = new MapperDefinitionScanner(registry);
  10. scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
  11. scanner.setResourceLoader(resourceLoader);
  12. scanner.doScan("com.alibaba.demon.mapper");
  13. }
  14. }

3.MapperDefinitionScanner 继承类ClassPathBeanDefinitionScanner

  1. public class MapperDefinitionScanner extends ClassPathBeanDefinitionScanner {
  2. public MapperDefinitionScanner(BeanDefinitionRegistry registry) {
  3. super(registry);
  4. }
  5. public void doScan(String packages) {
  6. super.doScan(packages);
  7. }
  8. }

4.使用import 注解引入 MapperDefinitionRegistrar

  1. @Configuration
  2. @Import(MapperDefinitionRegistrar.class)
  3. public class MapperAutoConfig {
  4. }

5.定义一个类,使用 mapper注解

  1. @Mapper
  2. public class WaybillMapper {
  3. }

6. 测试,可以正常跑通

  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class MapperAutoConfigTest {
  4. @Autowired
  5. private WaybillMapper waybillMapper;
  6. @Test
  7. public void test() {
  8. Assert.assertNotNull(waybillMapper.getClass());
  9. }
  10. }

Spring data jpa中的使用

jpa 的自动扫描注册就使用的是 ImportBeanDefinitionRegistrar

EnableJpaRepositories

  1. /**
  2. * Annotation to enable JPA repositories. Will scan the package of the
  3. annotated configuration class for Spring Data repositories by default.
  4. *
  5. * @author Oliver Gierke
  6. * @author Thomas Darimont
  7. */
  8. @Target(ElementType.TYPE)
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Documented
  11. @Inherited
  12. @Import(JpaRepositoriesRegistrar.class)
  13. public @interface EnableJpaRepositories {
  14. String[] value() default {};
  15. String[] basePackages() default {};
  16. Class<?>[] basePackageClasses() default {};
  17. Filter[] includeFilters() default {};
  18. Filter[] excludeFilters() default {};
  19. String repositoryImplementationPostfix() default "Impl";
  20. String namedQueriesLocation() default "";
  21. Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
  22. Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
  23. /**
  24. * Configure the repository base class to be used to create repository proxies for this particular configuration.
  25. *
  26. * @return
  27. * @since 1.9
  28. */
  29. Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
  30. // JPA specific configuration
  31. /**
  32. * Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories
  33. * discovered through this annotation. Defaults to {@code entityManagerFactory}.
  34. *
  35. * @return
  36. */
  37. String entityManagerFactoryRef() default "entityManagerFactory";
  38. /**
  39. * Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
  40. * discovered through this annotation. Defaults to {@code transactionManager}.
  41. *
  42. * @return
  43. */
  44. String transactionManagerRef() default "transactionManager";
  45. /**
  46. * Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
  47. * repositories infrastructure.
  48. */
  49. boolean considerNestedRepositories() default false;
  50. /**
  51. * Configures whether to enable default transactions for Spring Data JPA repositories. Defaults to {@literal true}. If
  52. * disabled, repositories must be used behind a facade that's configuring transactions (e.g. using Spring's annotation
  53. * driven transaction facilities) or repository methods have to be used to demarcate transactions.
  54. *
  55. * @return whether to enable default transactions, defaults to {@literal true}.
  56. */
  57. boolean enableDefaultTransactions() default true;
  58. }

JpaRepositoriesRegistrar

  1. class JpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
  2. @Override
  3. protected Class<? extends Annotation> getAnnotation() {
  4. return EnableJpaRepositories.class;
  5. }
  6. @Override
  7. protected RepositoryConfigurationExtension getExtension() {
  8. return new JpaRepositoryConfigExtension();
  9. }
  10. }

RepositoryBeanDefinitionRegistrarSupport

  1. public abstract class RepositoryBeanDefinitionRegistrarSupport
  2. implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
  3. private @SuppressWarnings("null") @Nonnull ResourceLoader resourceLoader;
  4. private @SuppressWarnings("null") @Nonnull Environment environment;
  5. @Override
  6. public void setResourceLoader(ResourceLoader resourceLoader) {
  7. this.resourceLoader = resourceLoader;
  8. }
  9. @Override
  10. public void setEnvironment(Environment environment) {
  11. this.environment = environment;
  12. }
  13. /*
  14. * (non-Javadoc)
  15. * @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
  16. */
  17. public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
  18. Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
  19. Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
  20. Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
  21. // Guard against calls for sub-classes
  22. if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
  23. return;
  24. }
  25. AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(
  26. annotationMetadata, getAnnotation(), resourceLoader, environment, registry);
  27. RepositoryConfigurationExtension extension = getExtension();
  28. RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);
  29. RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
  30. environment);
  31. delegate.registerRepositoriesIn(registry, extension);
  32. }