前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring - BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean

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

作者头像
小小工匠
发布2022-12-01 16:52:51
6340
发布2022-12-01 16:52:51
举报
文章被收录于专栏:小工匠聊架构

文章目录


Pre

Spring Boot - 扩展接口一览


org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

代码语言:javascript
复制
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 before 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


接口的继承关系

接口方法

代码语言:javascript
复制
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

入参 为 接口 BeanDefinitionRegistry

主要看提供的接口方法,可以发现提供的方法来主要有注册、反注册、判断 等操作


BeanDefinitionRegistryPostProcessor在Spring中的应用

代码语言:javascript
复制
org.springframework.context.support.AbstractApplicationContext#refresh

继续

代码语言:javascript
复制
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

代码语言:javascript
复制
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();

    }
}

当然了 搞个接口和实现类

代码语言:javascript
复制
package com.artisan.bootspringextend.service;

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

    /**
     * 方法
     */
    void doSomething();
}
代码语言:javascript
复制
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");
    }
}

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

测试一下


多数据源实现

代码语言:javascript
复制
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;
	}
}
代码语言:javascript
复制
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);
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/11/27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • Pre
  • org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
  • 接口的继承关系
  • BeanDefinitionRegistryPostProcessor在Spring中的应用
  • 示例
    • 注册Bean
      • 多数据源实现
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档