ImportBeanDefinitionRegistrar 定义:
一个类实现 ImportBeanDefinitionRegistrar ,通过 configuration 注解和 import 注解可以动态注入一个bean 到spring 容器。mybatis mapper 类的扫码注册就是通过 ImportBeanDefinitionRegistrar 实现的。
spring 框架自动配置,与其他框架组件融合时,都是通过 ImportBeanDefinitionRegistrar 实现的。比如 feign的自动配置,MybatisAutoConfiguratio、以及很多以 Enable 开头的配置类。
package org.springframework.context.annotation;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;import org.springframework.core.type.AnnotationMetadata;/*** Interface to be implemented by types that register additional bean definitions when* processing @{@link Configuration} classes. Useful when operating at the bean definition* level (as opposed to {@code @Bean} method/instance level) is desired or necessary.** <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type* may be provided to the @{@link Import} annotation (or may also be returned from an* {@code ImportSelector}).** <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective* methods will be called prior to {@link #registerBeanDefinitions}:* <ul>* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}* </ul>** <p>See implementations and associated unit tests for usage examples.** @author Chris Beams* @since 3.1* @see Import* @see ImportSelector* @see Configuration*/public interface ImportBeanDefinitionRegistrar {/*** Register bean definitions as necessary based on the given annotation metadata of* the importing {@code @Configuration} class.* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be* registered here, due to lifecycle constraints related to {@code @Configuration}* class processing.* @param importingClassMetadata annotation metadata of the importing class* @param registry current bean definition registry*/public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);}
使用方法
1.一个类实现ImportBeanDefinitionRegistrar 接口;
2.registerBeanDefinitions 实现bean 的注册逻辑;
3.在 @configuration 注解的类上面使用 import 注解导入实现 ImportBeanDefinitionRegistrar 接口的类;
Demo
自动扫码注册包含 Mapper 注解的类到spring 容器
1.自定义注解
/*** @author: michael* @create: 2020/12/30*/@Inherited@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Mapper {}
2.定义一个类实现 ImportBeanDefinitionRegistrar 接口
通过重写registerBeanDefinitions 方法,把包含 mapper 注解的类扫描注册到 spring 容器。
public class MapperDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {MapperDefinitionScanner scanner = new MapperDefinitionScanner(registry);scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class));scanner.setResourceLoader(resourceLoader);scanner.doScan("com.alibaba.demon.mapper");}}
3.MapperDefinitionScanner 继承类ClassPathBeanDefinitionScanner
public class MapperDefinitionScanner extends ClassPathBeanDefinitionScanner {public MapperDefinitionScanner(BeanDefinitionRegistry registry) {super(registry);}public void doScan(String packages) {super.doScan(packages);}}
4.使用import 注解引入 MapperDefinitionRegistrar
@Configuration@Import(MapperDefinitionRegistrar.class)public class MapperAutoConfig {}
5.定义一个类,使用 mapper注解
@Mapperpublic class WaybillMapper {}
6. 测试,可以正常跑通
@SpringBootTest@RunWith(SpringRunner.class)public class MapperAutoConfigTest {@Autowiredprivate WaybillMapper waybillMapper;@Testpublic void test() {Assert.assertNotNull(waybillMapper.getClass());}}
Spring data jpa中的使用
jpa 的自动扫描注册就使用的是 ImportBeanDefinitionRegistrar
EnableJpaRepositories
/*** Annotation to enable JPA repositories. Will scan the package of theannotated configuration class for Spring Data repositories by default.** @author Oliver Gierke* @author Thomas Darimont*/@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(JpaRepositoriesRegistrar.class)public @interface EnableJpaRepositories {String[] value() default {};String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Filter[] includeFilters() default {};Filter[] excludeFilters() default {};String repositoryImplementationPostfix() default "Impl";String namedQueriesLocation() default "";Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;/*** Configure the repository base class to be used to create repository proxies for this particular configuration.** @return* @since 1.9*/Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;// JPA specific configuration/*** Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories* discovered through this annotation. Defaults to {@code entityManagerFactory}.** @return*/String entityManagerFactoryRef() default "entityManagerFactory";/*** Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories* discovered through this annotation. Defaults to {@code transactionManager}.** @return*/String transactionManagerRef() default "transactionManager";/*** Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the* repositories infrastructure.*/boolean considerNestedRepositories() default false;/*** Configures whether to enable default transactions for Spring Data JPA repositories. Defaults to {@literal true}. If* disabled, repositories must be used behind a facade that's configuring transactions (e.g. using Spring's annotation* driven transaction facilities) or repository methods have to be used to demarcate transactions.** @return whether to enable default transactions, defaults to {@literal true}.*/boolean enableDefaultTransactions() default true;}
JpaRepositoriesRegistrar
class JpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {@Overrideprotected Class<? extends Annotation> getAnnotation() {return EnableJpaRepositories.class;}@Overrideprotected RepositoryConfigurationExtension getExtension() {return new JpaRepositoryConfigExtension();}}
RepositoryBeanDefinitionRegistrarSupport
public abstract class RepositoryBeanDefinitionRegistrarSupportimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {private @SuppressWarnings("null") @Nonnull ResourceLoader resourceLoader;private @SuppressWarnings("null") @Nonnull Environment environment;@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}/** (non-Javadoc)* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)*/public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");Assert.notNull(resourceLoader, "ResourceLoader must not be null!");// Guard against calls for sub-classesif (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {return;}AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(annotationMetadata, getAnnotation(), resourceLoader, environment, registry);RepositoryConfigurationExtension extension = getExtension();RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,environment);delegate.registerRepositoriesIn(registry, extension);}
