当前位置: 首页 > 知识库问答 >
问题:

spring:如何在运行时管理用BeanFactoryPostProcessor创建的数据源?

虞华翰
2023-03-14

我正在用Spring Data JPA做一个项目。

我已经设法用BeanFactoryPostProcessor动态创建了datasource,并在使用AbstracTroutingDataSource登录时切换到所需的datasource。

现在我想在运行时做的是:

  1. 使用BeanFactoryPostProcessor获取动态数据源映射
  2. 创建新的数据源
  3. 将最近创建的数据源与其他数据源一起放在映射中

SpringContext-jpa.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
............
>
<!-- 
...
...
Spring Data JPA config 
...
...
-->

<!--    Parent abstract Datasource -->
<bean id="BasicdsCargestWeb" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

<!--  Generic Datasource   -->
<bean id="dsCargestWeb" class="com.cargest.custom.CargestRoutingDataSource">
    <property name="targetDataSources">
        <map>

        </map>
    </property>
    <property name="defaultTargetDataSource" ref="cargestnet1ds" />
</bean>

</beans>

DataSourCeregisteringBeanFactoryPostProcessor.java

@Component 
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 

html" target="_blank">public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {

    // this is my list of Datasources 
    List<Database> dbs = new ArrayList<Database>();

    /*
     * Hidden code, here i get my list of Datasources 
     */

    BeanDefinitionRegistry factory = (BeanDefinitionRegistry) beanFactory;
    BeanDefinitionBuilder datasourceDefinitionBuilder;

    for (Database db : dbs) {
        datasourceDefinitionBuilder = BeanDefinitionBuilder
                .childBeanDefinition("BasicdsCargestWeb") 
                .addPropertyValue("url", db.getUrl()+db.getName()+"?autoReconnect=true");

        factory.registerBeanDefinition("cargestnet"+db.getId()+"ds",
                datasourceDefinitionBuilder.getBeanDefinition());
    }


    // Configure the dataSource bean properties 
    MutablePropertyValues mpv = factory.getBeanDefinition("dsCargestWeb").getPropertyValues();

    // Here you can set the default dataSource 
    mpv.removePropertyValue("defaultTargetDataSource");
    mpv.addPropertyValue("defaultTargetDataSource", 
        new RuntimeBeanReference("cargestnet1ds")); 

    // Set the targetDataSource properties map with the list of connections 
    ManagedMap<String, RuntimeBeanReference> mm = (ManagedMap<String, RuntimeBeanReference>) mpv.getPropertyValue("targetDataSources").getValue();
    System.out.println("list size "+mm.size());

    mm.clear();

    for (Database db : dbs) {
         mm.put(db.getId().toString(), new RuntimeBeanReference("cargestnet"+db.getId()+"ds"));
    }
} 
} 

问题是BeanFactoryPostProcessor类正在使用ConfigurableListableBeanFactory作为BeanFactory。

我需要从另一个类(在运行时)访问相同的beanFactory,以便修改我的数据源映射(DscarGestWeb-->TargetDatasources-->map)。

谢谢

共有2个答案

东门玺
2023-03-14

通过RuntimeBeanReferenceManagedSet,可以将运行时集设置为BeanDefine。这是正确的使用方法

ManagedSet<RuntimeBeanReference> processorSet = new ManagedSet<RuntimeBeanReference>();
    for (String processorBeanName : processorBeanNames) {
        processorSet.add(new RuntimeBeanReference(processorBeanName));
    }
beanDefine.getPropertyValues().addPropertyValue(new PropertyValue(key, value));
郭翰墨
2023-03-14

我认为您可以尝试以下架构:

  • 工厂bean创建数据源的初始映射
  • abstractroutingDataSource实现保存DataSources的映射,并在最初注入上述工厂bean

这样,如果以后需要添加新的DataSource,可以从AbstracTroutingDataSource实现获取映射并将其直接添加到其中,或者在AbstracTroutingDataSource实现中放置一个方法以添加新的DataSource

作为上述的变体,您可以创建一个类似的abstractroutingDataSource实现,以及另一个注入它的bean,它在其init-method中计算DataSources映射,并在路由数据源中对其进行传输。

 类似资料:
  • 问题内容: 我正在使用Hibernate租约,并且每次用户登录时,我都将数据库更改为其用户名(SQLite)。可悲的是,有时数据库不存在,我需要创建它。 问题是我不知道如何在运行时在数据库中创建所有表。 通常,Hibernete为此创建数据库: 问题答案: 创建数据库之后,可以为此使用SchemaExport导出要在新创建的数据库中创建的实体。基本步骤如下。如何获取配置的属性并不重要。 Javad

  • 我正在连接多个数据源,但有时某些数据源可能处于脱机状态,此时我在应用程序上遇到错误,应用程序在启动时失败。 我想在启动时跳过数据源配置...我已经尝试了几种方法 application.properties,我也试着添加 到主类,但它仍然试图配置数据源。 我还尝试在所有方法和构造函数上使用@Lazy注释,如下所示,但在创建efEntityManagerFactory时仍然出错 对于不同数据源的不同

  • 我想创建一个Spring Bean Factory后处理器,将Bean添加到当前的ApplicationContext中。 我的中有很多Web-Services定义,我想尽可能地减少。 配置如下所示: 因此,我用以下bean定义创建了一个@Configuration类: 这好多了!,但是我想把它减少得更多,所以我想创建一个名为@WebService定义的注释,并添加一个BeanFactoryPos

  • 我们需要执行从一个数据库到其他数据库的数据移动,并为此探索spring batch。我们应用程序的用户选择源数据源和目标数据源,以及需要为其移动数据的表列表。 在以下方面需要帮助: 构建作业所需的信息在运行时来自我们的web应用程序-包括数据源详细信息和表名列表。我们希望通过将这些详细信息发送到job builder模块来创建一个新作业,并使用JobLauncher启动它。我们如何编写这个job

  • 问题内容: 该FactoryBean的可用于以编程方式创建,这可能需要复杂的实例化逻辑的对象。 但是,似乎由所 创建 的bean 并没有成为春季管理的。这种解释正确吗?如果是这样,是否有任何不错的解决方法?包含一个简短的代码示例来说明我的问题。 ApplicationContext: 工厂实施: 工厂创建的类: 问题答案: 由创建的对象 是 由Spring管理,而不是由Spring实例化或配置。通

  • 我试图根据用户输入动态创建一个单声道序列。我正在调用rest API并在Mono中获得ClientResponse。我的用例是在一个序列中调用2个或多个API,下一个API的输入负载取决于前一个API的输出。 我的硬编码序列操作看起来像 这是很好的工作,但我想让这个方法通用。我想从用户的POJO列表中获取所有必需的参数,并基于列表输入创建flatMap序列。因此,如果列表的长度为2,则将有2个平面