Spring - BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean
Posted 小小工匠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring - BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean相关的知识,希望对你有一定的参考价值。
文章目录
- Pre
- org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
- 接口的继承关系
- BeanDefinitionRegistryPostProcessor在Spring中的应用
- 示例
Pre
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);
以上是关于Spring - BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean的主要内容,如果未能解决你的问题,请参考以下文章
Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC
学习笔记——Spring简介;Spring搭建步骤;Spring的特性;Spring中getBean三种方式;Spring中的标签
Spring框架--Spring事务管理和Spring事务传播行为