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;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public 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注解
@Mapper
public class WaybillMapper {
}
6. 测试,可以正常跑通
@SpringBootTest
@RunWith(SpringRunner.class)
public class MapperAutoConfigTest {
@Autowired
private WaybillMapper waybillMapper;
@Test
public void test() {
Assert.assertNotNull(waybillMapper.getClass());
}
}
Spring data jpa中的使用
jpa 的自动扫描注册就使用的是 ImportBeanDefinitionRegistrar
EnableJpaRepositories
/**
* Annotation to enable JPA repositories. Will scan the package of the
annotated 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 {
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableJpaRepositories.class;
}
@Override
protected RepositoryConfigurationExtension getExtension() {
return new JpaRepositoryConfigExtension();
}
}
RepositoryBeanDefinitionRegistrarSupport
public abstract class RepositoryBeanDefinitionRegistrarSupport
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private @SuppressWarnings("null") @Nonnull ResourceLoader resourceLoader;
private @SuppressWarnings("null") @Nonnull Environment environment;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public 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-classes
if (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);
}