Spring - BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean

简介: BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean

@[toc]

在这里插入图片描述


Pre

Spring Boot - 扩展接口一览

在这里插入图片描述


org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

package org.springframework.beans.factory.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;

/**
 * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     * @param registry the bean definition registry used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

BeanDefinitionRegistryPostProcessork可以在加载到项目中的beanDefinition之后执行,提供一个补充的扩展点。

举个例子: 动态注册自己的beanDefinition,加载classpath之外的bean


接口的继承关系

在这里插入图片描述

接口方法

void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

入参 为 接口 BeanDefinitionRegistry

在这里插入图片描述
主要看提供的接口方法,可以发现提供的方法来主要有注册、反注册、判断 等操作


BeanDefinitionRegistryPostProcessor在Spring中的应用

org.springframework.context.support.AbstractApplicationContext#refresh

在这里插入图片描述
在这里插入图片描述

继续

boolean reiterate = true;
while (reiterate) {
    reiterate = false;
    //查出所有实现了BeanDefinitionRegistryPostProcessor接口的bean名称
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
        //前面的逻辑中,已经对实现了PriorityOrdered和Ordered的bean都处理过了,因此通过processedBeans过滤,processedBeans中没有的才会在此处理
        if (!processedBeans.contains(ppName)) {
            //根据名称和类型获取bean
            BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
            //把已经调用过postProcessBeanDefinitionRegistry方法的bean全部放在registryPostProcessors中
            registryPostProcessors.add(pp);
            //把已经调用过postProcessBeanDefinitionRegistry方法的bean的名称全部放在processedBeans中
            processedBeans.add(ppName);
            //执行此bean的postProcessBeanDefinitionRegistry方法
            pp.postProcessBeanDefinitionRegistry(registry);
            //改变退出while的条件
            reiterate = true;
        }
    }
}

//registryPostProcessors中保存了所有执行过postProcessBeanDefinitionRegistry方法的bean,
//现在再来执行这些bean的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
//regularPostProcessors中保存的是所有入参中带来的BeanFactoryPostProcessor实现类,并且这里面已经剔除了BeanDefinitionRegistryPostProcessor的实现类,现在要让这些bean执行postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

示例

注册Bean

package com.artisan.bootspringextend.testextends;

import com.artisan.bootspringextend.service.ArtisanService;
import com.artisan.bootspringextend.service.ArtisanServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Configuration;

/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/11/26 23:30
 * @mark: show me the code , change the world
 */

@Slf4j
@Configuration
public class ExtendBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        log.info("---->postProcessBeanDefinitionRegistry");

        //The service implementation
        RootBeanDefinition beanDefinition =
                new RootBeanDefinition(ArtisanServiceImpl.class);
        //The service interface
        beanDefinition.setTargetType(ArtisanService.class);
        beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION);
        registry.registerBeanDefinition("artisanService", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("---->postProcessBeanFactory");

        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("artisanService");

        log.info(beanDefinition.getBeanClassName());

        ArtisanService artisanService = (ArtisanService) beanFactory.getBean("artisanService");

        artisanService.doSomething();

    }
}
    

当然了 搞个接口和实现类

package com.artisan.bootspringextend.service;

/**
 * @author artisan
 */
public interface ArtisanService {

    /**
     * 方法
     */
    void doSomething();
}

package com.artisan.bootspringextend.service;

import lombok.extern.slf4j.Slf4j;

/**
 * @author 小工匠
 * @version 1.0
 * @date 2022/11/27 10:08
 * @mark: show me the code , change the world
 */

@Slf4j
public class ArtisanServiceImpl implements ArtisanService {

    @Override
    public void doSomething() {
        log.info("-------> ArtisanServiceImpl#doSomething called");
    }
}

这里的实现类没有加注解哦

测试一下

在这里插入图片描述


多数据源实现

import org.springframework.core.env.ConfigurableEnvironment;

/**
 * 
 * DataSourceProperties stores the properties for a particular datasource.
 * In a normal, non-dynamic bean program these properties could come from
 * @ConfigurationProperties but this won't where we want to support a dynamic
 * prefix.
 * 
 * The properties are 
 * 
 * prefix1.datasource.driver=
 * prefix1.datasource.url=
 * prefix1.datasource.username=
 * prefix1.datasource.password=
 * 
 * prefix2.datasource.driver=
 * prefix2.datasource.url=
 * prefix2.datasource.username=
 * prefix2.datasource.password=
 * 
 * .
 * .
 * .
 * prefixn.datasource.driver=
 * prefixn.datasource.url=
 * prefixn.datasource.username=
 * prefixn.datasource.password=
 * 
 * Each instance of this class stores the properties for a prefix.
 * 
 */
public class DataSourceProperties {

    private String driver;
    private String url;
    private String username;
    private String password;
    private String prefix;
    private Boolean primary=false;
    
    private ConfigurableEnvironment environment;
    
    private static String propertyBase="datasource";
    
    public DataSourceProperties(ConfigurableEnvironment environment,
            String prefix) {
        this.prefix = prefix;
        this.environment = environment;
        driver = getProperty("driver");
        url = getProperty("url");
        username = getProperty("username");
        password = getProperty("password");
        primary = getProperty("primary",Boolean.class);
        
    }
    
    public static boolean isUrlProperty(String property) {
        if(property.endsWith(propertyBase + ".url")) {
            return true;
        }
        return false;
    }
    
        
    public String getDriver() {
        return driver;
    }

    public String getUrl() {
        return url;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getPrefix() {
        return prefix;
    }

    public ConfigurableEnvironment getEnvironment() {
        return environment;
    }

    public static String getPropertyBase() {
        return propertyBase;
    }
    public Boolean getPrimary() {
        return primary;
    }
    private String getProperty(String property) {
        return getProperty(property,String.class);
    }
    private<T> T getProperty(String property,Class<T> type) {
        
        T value = environment.getProperty(prefix + "." + propertyBase + "." + property,type);
        if(value == null) {
            throw new IllegalStateException(prefix + "." + propertyBase + "." + property +" is not found" );
        }
        return value;
    }
}
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;

@Configuration
public class DataSourceConfiguration {

    static private Logger logger = Logger.getLogger(DataSourceConfiguration.class);
    
    /**
     * 
     * Create a beanPostProcessor , @Bean for adding the dynamic beans.
     */
    @Bean
    static BeanDefinitionRegistryPostProcessor beanPostProcessor(final ConfigurableEnvironment environment) {
        return new BeanDefinitionRegistryPostProcessor() {

            public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
                // TODO Auto-generated method stub
                
            }

            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
                createDynamicBeans(environment,beanRegistry);
                
            }
            
        };
    }
    
    /**
     * 
     * @param environment The environment which properties can be extracted from.
     * @return A map of DataSourceProperties for each prefix.
     */
    static private Map<String,DataSourceProperties> parseProperties(ConfigurableEnvironment environment) {
        Map<String,DataSourceProperties> propertyMap = new HashMap<>();        
        for(PropertySource source : environment.getPropertySources()) {
            if(source instanceof EnumerablePropertySource ) {
                EnumerablePropertySource propertySource = (EnumerablePropertySource) source;
                for(String property : propertySource.getPropertyNames()) {
                    if(DataSourceProperties.isUrlProperty(property)) {
                        String prefix = extractPrefix(property);
                        propertyMap.put(prefix, new DataSourceProperties(environment,prefix));
                    }
                }
            }
        }
        return propertyMap;
    }
    
    static private void createDynamicBeans(ConfigurableEnvironment environment,BeanDefinitionRegistry beanRegistry) {
        Map<String,DataSourceProperties> propertyMap = parseProperties(environment);
        for(Map.Entry<String,DataSourceProperties> entry : propertyMap.entrySet()) {
            registerDynamicBean(entry.getKey(),entry.getValue(),beanRegistry);
        }
    }
    
    
    /**
     * This function will create the dynamic bean definitions.
     * @param prefix  The prefix for the beans we are creating.
     * @param dsProps  The properties for the datasource
     * @param beanRegistry  The bean registry we add the beans to
     */
    static private void registerDynamicBean(String prefix, DataSourceProperties dsProps,BeanDefinitionRegistry beanRegistry) {    
        logger.info("Registering beans for " + prefix);
        BeanDefinition dataSourceBeanDef = BeanDefinitionBuilder.genericBeanDefinition(BasicDataSource.class)
                .addPropertyValue("url",dsProps.getUrl())
                .addPropertyValue("username", dsProps.getUsername())
                .addPropertyValue("password", dsProps.getPassword())
                .addPropertyValue("driverClassName", dsProps.getDriver())                
                .getBeanDefinition();
        if(dsProps.getPrimary()) {
            dataSourceBeanDef.setPrimary(true);
        }
        beanRegistry.registerBeanDefinition("datasource_" + prefix, dataSourceBeanDef);
        if(dsProps.getPrimary()) {
            beanRegistry.registerAlias("datasource_" + prefix, "dataSource");
        }
        BeanDefinition repositoryBeanDef = BeanDefinitionBuilder.genericBeanDefinition(Repository.class)
                .addConstructorArgReference("datasource_" + prefix)
                .getBeanDefinition();
        beanRegistry.registerBeanDefinition("repository_" + prefix, repositoryBeanDef);
        
    }
    
    
    
    static private String extractPrefix(String property) {
        int idx = property.indexOf(".");
        return property.substring(0, idx);
    }
}

在这里插入图片描述

相关文章
|
16天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
70 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
30天前
|
存储 安全 Java
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
83 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
44 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
88 1
|
1月前
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
228 0
|
5月前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
57 2
|
5月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
62 0
下一篇
无影云桌面