简体   繁体   English

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

[英]Spring : How to manage datasources created with BeanFactoryPostProcessor, at runtime?

I am working on a projet using Spring Data JPA. 我正在使用Spring Data JPA开发projet。

I've managed to dynamicly create datasource with BeanFactoryPostProcessor and switch to the desired one when i log in using AbstractRoutingDataSource . 我设法使用BeanFactoryPostProcessor动态创建数据源,并在使用AbstractRoutingDataSource登录时切换到所需的数据源。

Now what i want to do in runtime is to: 现在我想在运行时中执行以下操作:

  1. Get the map of the dynamically datasources with BeanFactoryPostProcessor 使用BeanFactoryPostProcessor获取动态数据源的地图
  2. Create a new datasource 创建一个新的数据源
  3. Put the recently created datasource in the map along with the others 将最近创建的数据源以及其他数据源放入地图中

springContext-jpa.xml 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 DatasourceRegisteringBeanFactoryPostProcessor.java

@Component 
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 

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"));
    }
} 
} 

The problem is that the BeanFactoryPostProcessor class is using ConfigurableListableBeanFactory as a beanFactory. 问题在于BeanFactoryPostProcessor类使用ConfigurableListableBeanFactory作为beanFactory。

I need to access that same beanFactory from another class (in runtime) in order to modify my map of datasources (dsCargestWeb --> targetDataSources --> map). 我需要从另一个类(在运行时)中访问相同的beanFactory,以修改我的数据源映射(dsCargestWeb-> targetDataSources->映射)。

Thanks 谢谢

I think you could try the following architecture : 我认为您可以尝试以下架构:

  • a factory bean creates the initial map of DataSourceS 工厂bean创建DataSourceS的初始映射
  • the AbstractRoutingDataSource implementation holds the map of the DataSourceS , and is initially injected with above factory bean AbstractRoutingDataSource实现保存了DataSourceS的映射,并最初注入了上述工厂bean

That way, if you later need to add a new DataSource , you can either get the map from the AbstractRoutingDataSource implementation and add it directly to it, or put a method in the AbstractRoutingDataSource implementation to add a new DataSource . 这样,如果以后需要添加一个新的DataSource ,你可以得到从地图AbstractRoutingDataSource实施,并直接将其添加到它,或者把一个方法在AbstractRoutingDataSource实现添加新的DataSource

As a variant from the above, you can create a similar AbstractRoutingDataSource implementation, and another bean injected with it, that in its init-method computes the DataSourceS map and strores it in the routing datasource. 作为上述方法的一个变体,您可以创建一个类似的AbstractRoutingDataSource实现,并向其注入另一个bean,该bean的init-method中将计算DataSourceS映射并将其散布在路由数据源中。

With RuntimeBeanReference and ManagedSet , you can set a runtime set to your beanDefine . 使用RuntimeBeanReferenceManagedSet ,可以将运行时设置设置为beanDefine This is the proper way to use these: 这是使用这些的正确方法:

ManagedSet<RuntimeBeanReference> processorSet = new ManagedSet<RuntimeBeanReference>();
    for (String processorBeanName : processorBeanNames) {
        processorSet.add(new RuntimeBeanReference(processorBeanName));
    }
beanDefine.getPropertyValues().addPropertyValue(new PropertyValue(key, value));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM