深入解析Spring BeanDefinition:理解加载、解析与注册的全过程

BeanDefinition 概述

BeanDefinition 是 Spring 用来描述和定义应用程序中的 Bean。在 Spring 中,Bean 是应用程序中被 Spring 容器所管理的对象,BeanDefinition 则用来描述这些被管理的对象的属性和行为。

BeanDefinition 包含了以下信息:

  1. Bean 的类名
  2. Bean 的作用域(singleton、prototype 等)
  3. Bean 的生命周期设置(初始化方法、销毁方法)
  4. 是否懒加载
  5. 依赖关系
  6. 其他属性

BeanDefinition 的作用包括:

  1. 定义 Bean 的配置元数据:BeanDefinition 定义了每个 Bean 的配置信息,包括类名、依赖关系、初始化方法、销毁方法等。它描述了如何创建和配置一个特定的 Bean 实例。
  2. 实例化 Bean:BeanDefinition 充当了实例化 Bean 的指南。Spring IoC 容器根据 BeanDefinition 中的配置信息来创建 Bean 实例,并在需要时将其初始化。
  3. 管理 Bean 的生命周期:BeanDefinition 中定义了 Bean 的生命周期方法,如初始化方法和销毁方法,Spring IoC 容器负责调用这些方法,以确保 Bean 在适当的时候进行初始化和销毁。
  4. 处理 Bean 的依赖关系:BeanDefinition 中包含了 Bean 之间的依赖关系,Spring IoC 容器使用这些信息来解析和管理 Bean 之间的依赖关系,确保它们在合适的时间被注入和初始化。
  5. 支持各种配置方式:BeanDefinition 支持多种配置方式,包括 XML 配置、注解配置和 Java 配置。它为开发者提供了灵活的选择,可以根据项目的需求和个人喜好选择合适的配置方式。

BeanDefinition 的继承结构

在 Spring 框架中,BeanDefinition 接口是用来表示一个 Bean 的定义(包括配置信息)的接口。它定义了一个 Bean 的属性、构造函数参数、依赖关系等元数据信息。

BeanDefinition 接口的常见实现类包括:

  1. GenericBeanDefinition:通用的 BeanDefinition 实现类,用于表示大多数的 Bean 定义。GenericBeanDefinition 提供了丰富的属性设置方法,可以通过这些方法设置 Bean 的各种属性信息。
  2. RootBeanDefinition:AbstractBeanDefinition 的直接子类,通常用于定义独立的 Bean,即非继承关系的 Bean,意味着无法处理父子 Bean。
  3. ChildBeanDefinition:AbstractBeanDefinition 的直接子类,通常用于定义继承关系中的子 Bean,即通过标签的 parent 属性来继承父 Bean 的配置信息的子 Bean。
  4. AnnotatedGenericBeanDefinition:用于表示使用注解进行配置的 Bean 的定义,它实现了 GenericBeanDefinition 接口,并提供了对注解信息的访问方法。
  5. ScannedGenericBeanDefinition:用于表示通过组件扫描(component scanning)发现的 Bean 定义的实现类。它用于将扫描到的类转换为 BeanDefinition 对象。

在 Spring 2.5 之后就不在使用 RootBeanDefinition 和 ChildBeanDefinition,而是推荐直接可以处理父子 Bean 的 GenericBeanDefinition 了。

BeanDefinitionHolder 与 BeanDefinition 的关系

BeanDefinitionHolder 是 Spring Framework 中的一个类,用于持有一个 BeanDefinition 对象以及与之相关联的名称。在 Spring 内部有大量的地方都是基于 Bean 的名称去操作,所以将 BeanDefinition 的名称和别名提取出来,封装成 BeanDefinitionHolder 就是为了方便管理和操作 BeanDefinition。

BeanDefinitionHolder 的典型用法是在编写自定义的 BeanFactoryPostProcessor 或 BeanDefinitionRegistryPostProcessor 时,用来注册或修改 Bean 定义。

BeanDefinition 的解析和创建

BeanDefinitionReader 接口体系

BeanDefinition 的解析过程离不开 BeanDefinitionReader 接口。在 Spring 框架中,BeanDefinitionReader 接口是用于读取和解析 Bean 定义信息的接口。它定义了一些方法,用于从不同的资源(如 XML 文件、注解或者其他方式)中读取 Bean 定义,并将其注册到容器中。

Spring 框架为不同类型的资源提供了不同的 BeanDefinitionReader 实现,以便读取和解析对应类型的 Bean 定义。常见的实现包括:

  • XmlBeanDefinitionReader:用于从 XML 文件中读取 Bean 定义。
  • PropertiesBeanDefinitionReader:用于从属性文件中读取 Bean 定义。
  • GroovyBeanDefinitionReader:与 Groovy 编程语言的整合。

这里要特别注意 Spring 设计不合理的一个地方,就是对于注解类型的 BeanDefinition 的解析类 AnnotatedBeanDefinitionReader 并不是属于这个 BeanDefinitionReader 体系,而是实实在在就是一个单独的类。

XmlBeanDefinitionReader 的工作机制

这个 XML 解析创建 BeanDefinition 的过程前面文章已经详细介绍:《解码Spring XML初始化流程:逐步分析容器启动的原理与实践》,简化来说就是首先通过 SAX 技术进行 XML 文件的 DOM 解析,之后根据解析出的一堆 Bean 标签(又区分了默认标签和自定义标签),循环创建内部持有 GenericBeanDefinition 的 BeanDefinitionHolder,然后注册到 DefaultListableBeanFactory 的 beanDefinitionMap 中。

AnnotatedBeanDefinitionReader 的工作机制

AnnotatedBeanDefinitionReader 主要在 AnnotationConfigApplicationContext 中使用,当我们需要通过配置类的方式来创建上下文的时候,会使用以下代码:

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

在 AnnotationConfigApplicationContext 的此构造方法中有一核心步骤就是去注册配置类。

private final AnnotatedBeanDefinitionReader reader;

public void register(Class<?>... annotatedClasses) {
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
    this.reader.register(annotatedClasses);
}

这里就是 AnnotatedBeanDefinitionReader 发挥作用的地方,用于注册配置 Bean(创建 BeanDefinition 并注册到上下文)。这里传入的是可变参数是 Spring 对于多个配置 Bean 情况的考虑。具体逻辑可以查看 AnnotatedBeanDefinitionReader 的 doRegisterBean 方法:

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
                        @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {

    // 传入要注册的 Bean 的类,直接 new 了一个 AnnotatedGenericBeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }

    abd.setInstanceSupplier(instanceSupplier);
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    // 生成 Bean 的名称
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            }
            else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            }
            else {
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }
    for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
        customizer.customize(abd);
    }
	// 包装成 BeanDefinitionHolder
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    // 创建代理
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 注册到上下文工厂
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

可以看出,AnnotatedBeanDefinitionReader 要比 XmlBeanDefinitionReader 的处理要简单很多,直接 new 出 AnnotatedGenericBeanDefinition,然后包装成 BeanDefinitionHolder,最后注册到上下文工厂就完事了。

ClassPathBeanDefinitionScanner 工作机制

ClassPathBeanDefinitionScanner 是 Spring 框架中用于扫描类路径下的特定包,识别和注册 BeanDefinition 的工具类,它是 Spring 框架中的一个重要组件,通常在基于注解的配置中使用。通过 ClassPathBeanDefinitionScanner 创建和注册的 BeanDefinition 是 ScannedGenericBeanDefinition 类型的。

ClassPathBeanDefinitionScanner 通常与注解驱动的组件扫描一起使用,例如 @Component、@Service、@Repository 等注解,它可以自动识别被这些注解标记的类,并将其注册为 Spring 容器中的 Bean。这样可以减少配置文件的冗余,提高开发效率。为什么会自动识别这些注解?在 ClassPathBeanDefinitionScanner 中就有这个逻辑:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
      Environment environment, ResourceLoader resourceLoader) {
   this.registry = registry;
   // 默认 true
   if (useDefaultFilters) {
      registerDefaultFilters();
   }
   setEnvironment(environment);
   setResourceLoader(resourceLoader);
}

protected void registerDefaultFilters() {
   // includeFilters中是允许过滤的条件,指定扫描 @Component 注解及其子注解
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   
   // 同时支持扫描 Java EE 6 的javax.annotation.ManagedBean
   try {
      this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) 
                                                        ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
   } catch (ClassNotFoundException ex) {}
   
   // 同时支持扫描 JSR-330 的 javax.inject.Named
   try {
      this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) 
                                                        ClassUtils.forName("javax.inject.Named", cl)), false));
   } catch (ClassNotFoundException ex) {}
}

ClassPathBeanDefinitionScanner 主要在 AnnotationConfigApplicationContext 中使用,当我们需要通过配置扫描包的方式来创建上下文的时候,会使用以下代码:

ApplicationContext context = new AnnotationConfigApplicationContext("world.xuewei");

在 AnnotationConfigApplicationContext 的此构造方法中有一核心步骤就是去扫描包。

private final ClassPathBeanDefinitionScanner scanner;

public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    this.scanner.scan(basePackages);
}

这里就是 ClassPathBeanDefinitionScanner 发挥作用的地方,用于扫描、注册指定包下的所有 Bean(创建 BeanDefinition 并注册到上下文)。这里传入的是可变参数是 Spring 对于多个扫描包情况的考虑。具体逻辑可以查看 ClassPathBeanDefinitionScanner 的 doScan 方法:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 存储创建出的 BeanDefinitionHolder
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 依次扫描每个包中的相关注解,创建获取到所有的 BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成 Bean 的名称
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                // 包装成 BeanDefinitionHolder
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // 创建代理
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                 // 注册到上下文工厂
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

再往底层追源码,发现扫描的过程会交给 scanCandidateComponents 方法:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 根据指定包名,生成包搜索路径
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        
        // 资源加载器 加载搜索路径下的所有 class 转换为 Resource[],拿着上面的路径,就可以 getResources 获取出所有的类,这个很强大
        // 真正干事的为:PathMatchingResourcePatternResolver#getResources 方法
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        for (Resource resource : resources) {
            // 文件必须可读 否则直接返回空了
            if (resource.isReadable()) {
                try {
                    //读取类的注解信息和类信息,两大信息储存到 MetadataReader
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 根据 TypeFilter 过滤排除组件。扫描 @Component 注解及其子注解、Javax 的 ManagedBean、Named 注解
                    if (isCandidateComponent(metadataReader)) {
                        // 把符合条件的类转换成 BeanDefinition,这里就直接 new 了 ScannedGenericBeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        // 再次对类的类型进行判断,判断是否符合 Bean 定义
                        if (isCandidateComponent(sbd)) {
                            candidates.add(sbd);
                        }
                    }
                } 
            }
        }
    }
    return candidates;
}

在判断是否为扫描的类的时候,用到了前面在构造方法中配置的过滤器,即扫描 @Component 注解及其子注解、Javax 的 ManagedBean、Named 注解,通过下面的源码可以发现,excludeFilters 排除策略的优先级要高于 includeFilters 包含策略。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

Spring 中的 @ComponentScan 和 MyBatis 的 @MapperScan 底层都是通过 ClassPathBeanDefinitionScanner 实现的,不过 MyBatis 的 @MapperScan 使用的是 MyBatis 提供的 ClassPathBeanDefinitionScanner 的子类 ClassPathMapperScanner。

BeanDefinitionBuilder 工作机制

通过使用 BeanDefinitionBuilder,我们可以以编程的方式动态地创建和配置 Bean 定义,而不是在 XML 或注解中硬编码。这对于需要根据不同条件创建不同类型的 Bean,或者需要在运行时动态配置 Bean 属性的情况非常有用。

@Test
public void beanDefinitionBuilderTest() {
    // 手动创建 BeanDefinition
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Teacher.class);
    // 配置属性
    builder.addPropertyValue("name", "张三");
    builder.addPropertyValue("age", 25);
    GenericBeanDefinition beanDefinition = (GenericBeanDefinition) builder.getBeanDefinition();
    // 封装 BeanDefinitionHolder
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, "teacher");
    // 调用工具类注册到上下文,Spring 底层也都是调用的这行代码
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, context);
    // 也可以通过这行代码直接注册 BeanDefinition
    context.registerBeanDefinition("teacher", beanDefinition);
    System.out.println(context.getBean("teacher"));
}

BeanDefinition 的注册过程

通过上面的 XmlBeanDefinitionReader、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner、BeanDefinitionBuilder,我们发现在创建好 BeanDefinition 后,紧跟着的就是要将其注册到上下文工厂。

这几种方式最后的注册都是交给了一个工具类的方法 BeanDefinitionReaderUtils#registerBeanDefinition:

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    throws BeanDefinitionStoreException {
    
    // 获取 Bean 的名字(也就是指定的 id)
    String beanName = definitionHolder.getBeanName();
    // 调用工厂的注册方法,将 BeanDefinitionHolder 中的 BeanDefinition 注册到工厂
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // 调用工厂的注册方法,将 BeanDefinition 别名也进行注册
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

BeanDefinitionRegistry 是一个接口,而前面我们介绍的 DefaultListableBeanFactory 就是他的实现,其实现的 registerBeanDefinition 方法将 BeanDefinition 存放在了内部维护的 beanDefinitionMap 之中。

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null"
    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        } catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        this.beanDefinitionMap.put(beanName, beanDefinition);
    } else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        } else {
            // 将 BeanDefinition 存在了 工厂的 beanDefinitionMap 中
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }
    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

Spring 注册 Bean 的方式

再次总结一下 Spring 注册 Bean 的方式,不管使用哪种方式,最后都是通过一些手段,创建 BeanDefiniton,然后注册到上下文工厂中。

XML 的方式

程序员开发的过程,使用最为传统的配置方式 XML,那么 Spring 最终就会通过 XML 的配置进行注册。不过目前这种方式,正在逐步替代。

由 XmlBeanDefinitionReader 负责 BeanDefinition 的创建,使用 BeanDefinitionReaderUtils#registerBeanDefinition 工具方法进行注册。

注解的方式

@Component 及其衍生注解

@Component、@Configuration、@Service、@Controller、@Repostory等,这种方式目前 Spring 在开发过程建议我们使用这种注解的注册方式来注册程序员自己创建的类型。

@Bean 注解

使用 @Bean 这种方式也是目前 Spring 在开发过程建议我们使用的注册方式,这种注册类型的特点是用于注册别的框架(非 Spring)组件提供的类型,例如 MyBatis 的 SqlSessionFactory、DataSource 等。

扫描包方式

使用 new AnnotationConfigApplicationContext("com.application") 或者 @ComponentScan("com.application") 也可以实现 Bean 的注册。

由 ClassPathBeanDefinitionScanner 负责扫描指定注解(@Component 及其衍生注解)的 Bean,创建 BeanDefinition,然后使用 BeanDefinitionReaderUtils#registerBeanDefinition 工具方法进行注册。

@Import(普通类)

一般情况下使用 Import 注解,场景主要应用于框架的底层封装,避免像用户暴露注册的 Bean 的细节。

@import 普通 Java 类,我们可以实现当前类的注册,这个类不需要任何的注解的标记。例如:@Import(User.class),不过这种注册单个类的方式通常都是不常用的。

@Import(ImportSelector.class)

ImportSelector 是 Spring 框架中的一个接口,它用于实现动态选择需要被导入到容器中的配置类。在基于注解的配置中,ImportSelector 可以用于根据特定的条件或规则,选择性地导入一组配置类或者 Bean。

ImportSelector 接口包含一个方法:

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

selectImports 方法接收一个 AnnotationMetadata 参数,该参数表示使用了 @Import 注解的配置类的元数据信息。

我们需要动手创建一个 ImportSelector 的实现类,然后根据自己的规则,返回一个字符串的数组,里面包含了要注册给 Spring 的类的全限定类名,也就是我们使用 ImportSelector 的前提是,我们能够明确的知道要注册的类的全限定类名,而由于我们返回的只是字符串,实际的 BeanDefinition 的创建并不是在这里,无法给 BeanDefinition 设置一些属性,所以相对也并不是那么的灵活,如下:

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{
                Teacher.class.getName(),
                "world.xuewei.entity.Leader"
        };
    }
}

之后我们在配置类上添加注解:@Import(MyImportSelector.class) 即可。

@Import(ImportSelector.class) 通常在框架的底层都有很多地方应用了,这样可以避免像用户暴露底层要注册 Bean 的细节。

例如 SpringBoot 的自动装配注解 @EnableAutoConfiguration,底层就是 @Import({AutoConfigurationImportSelector.class}),而这个 AutoConfigurationImportSelector 显然就是实现了 ImportSelector 接口。

@Import(ImportBeanDefinitionRegister.class)

ImportBeanDefinitionRegistrar 是 Spring 框架中的一个接口,它允许用户在配置类中动态注册 BeanDefinition,从而向 Spring 容器中注入 Bean。通过实现 ImportBeanDefinitionRegistrar 接口,开发人员可以在运行时根据特定的条件或规则注册一些额外的 BeanDefinition,从而实现更加灵活和动态的配置。

ImportBeanDefinitionRegistrar 接口包含一个方法:

public interface ImportBeanDefinitionRegistrar {
    void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

registerBeanDefinitions 方法接收两个参数:

  • importingClassMetadata:表示使用了 @Import 注解的配置类的元数据信息。
  • registry:BeanDefinition 注册表,可以用来注册新的 BeanDefinition。

通过实现 ImportBeanDefinitionRegistrar 接口,并重写 registerBeanDefinitions 方法,开发人员可以在该方法中根据特定的条件或规则动态地注册 BeanDefinition。这样可以实现一些复杂的 Bean 注册逻辑,例如根据不同的条件注册不同的 Bean,或者根据配置文件中的属性动态注册 Bean 等。

显然这种方式相比 ImportSelector 要更加的灵活,我们已经拿到的工厂的注册器,那么往里面注册什么 BeanDefinition,注册多少个 BeanDefinition 那都是我们说的算了。使用方法如下:

public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar{

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 手动创建 BeanDefinition
        BeanDefinitionBuilder builder1 = BeanDefinitionBuilder.genericBeanDefinition(Teacher.class);
        // 配置属性
        builder1.addPropertyValue("name", "张三");
        builder1.addPropertyValue("age", 25);
        GenericBeanDefinition teacher = (GenericBeanDefinition) builder1.getBeanDefinition();
        registry.registerBeanDefinition("teacher", teacher);

        // 手动创建 BeanDefinition
        BeanDefinitionBuilder builder2 = BeanDefinitionBuilder.genericBeanDefinition(Leader.class);
        // 配置属性
        builder2.addPropertyValue("name", "李四");
        builder2.addPropertyValue("age", 35);
        GenericBeanDefinition leader = (GenericBeanDefinition) builder2.getBeanDefinition();
        registry.registerBeanDefinition("leader", leader);
    }
}

之后我们在配置类上添加注解:@Import(MyImportBeanDefinitionRegister.class) 即可。

MyBatis 的 @MapperScan 注解就是基于 ImportBeanDefinitionRegister 去实现的,向容器中注册一个 MapperScannerConfigurer,并为其设置各种属性。

  • 31
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值