8

我按照http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/文章成功地实现了数据库连接的动态变化。

但现在的问题是,我在由旧应用程序管理的配置文件中有一个数据库 url 列表。

有没有办法在 Spring 上下文中从值列表(即 Year2011DataSource、Year2012DataSource...)中创建 bean,并用刚刚创建的那些 bean 填充数据源 bean 的映射?

<!-- Property file located in the legacy application's folder -->
<context:property-placeholder location="file:///D:/config.properties" />

<!-- Shared data source properties are read from the config.properties file -->
<bean id="parentDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" abstract="true">
    <property name="driverClassName" value="${db.driver}" />
    <property name="username" value="${db.user}" />
    <property name="password" value="${db.password}" />
</bean>

<!-- Database urls by year -->
<bean id="Year2012DataSource" parent="parentDataSource">
    <property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2012" />
</bean>
<bean id="Year2011DataSource" parent="parentDataSource">
    <property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2011" />
</bean>
<bean id="Year2010DataSource" parent="parentDataSource">
    <property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2010" />
</bean>
<!-- ... and so on, these should instead be populated dynamically ... -->

<!-- DbConnectionRoutingDataSource extends AbstractRoutingDataSource -->
<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
    <property name="targetDataSources">
        <map key-type="int">
            <entry key="2011" value-ref="Year2011DataSource" />
            <entry key="2010" value-ref="Year2010DataSource" />
            <!-- ... and so on, these also should instead be populated dynamically ... -->
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="Year2012DataSource" />
</bean>
4

4 回答 4

8

我认为非常适合此要求的是自定义BeanFactoryPostProcessor - 读取遗留配置并在自定义 bean 工厂后处理器中生成数据源:

class MyDatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //Read in details from legacy properties.. build custom bean definitions and register with bean factory
        //for each legacy property...
            BeanDefinitionBuilder datasourceDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicDataSource.class).addPropertyValue("url", "jdbc..");
            beanFactory.registerBeanDefinition(datasourceDefinitionBuilder.getBeanDefinition());
    }
}
于 2012-08-23T13:39:34.243 回答
1

据我所知,没有使用 XML 配置的开箱即用解决方案。但是,在此答案中使用FactoryBeanSpring 中的抽象描述了实现此目的的简单解决方案。

于 2012-08-23T11:36:50.573 回答
1

============================================

按照 Biju 的提示,我的一切工作都像这样:

============================================

spring 配置中的“数据库 urls by year”部分不再存在,bean 是在 BeanFactoryPostProcessor 中创建的。

“dataSource” bean 将其属性设置为在 BeanFactoryPostProcessor 中替换的虚拟数据:

<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
    <property name="targetDataSources">
        <map key-type="String">
            <!-- Will be filled from the DatasourceRegisteringBeanFactoryPostProcessor -->
        </map>
    </property>
    <property name="defaultTargetDataSource" value="java:jboss/datasources/ExampleDS" />
</bean>

这是 BeanFactoryPostProcessor 的实现:

@Component
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        InitialContext ic = new InitialContext();

        // read the list of available JNDI connections
        NamingEnumeration<?> list = ic.listBindings(getJndiDSRoot());
        HashSet<String> jndiDataSources = new HashSet<String>();
        while (list.hasMore()) {
            /*... (ommitted for simplicity) ...*/
            connectionsList.put(key, value);
        }            

        BeanDefinitionRegistry factory = (BeanDefinitionRegistry) beanFactory;
        BeanDefinitionBuilder datasourceDefinitionBuilder;

        // Create new beans
        for (Entry<Integer, String> e : connectionsList.entrySet()) {
            datasourceDefinitionBuilder = BeanDefinitionBuilder
                    .childBeanDefinition("parentDataSource")
                    .addPropertyValue("url", e.getValue());

            factory.registerBeanDefinition("Year" + e.getKey() + "DataSource",
                    datasourceDefinitionBuilder.getBeanDefinition());
        }

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

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

        // Set the targetDataSource properties map with the list of connections
        ManagedMap<Integer, RuntimeBeanReference> mm = (ManagedMap<Integer, RuntimeBeanReference>) mpv.getPropertyValue("targetDataSources").getValue();
        mm.clear();

        // Fill the map with bean references to the newly created beans
        for (Entry<Integer, String> e : connectionsList.entrySet()) {
            mm.put(e.getKey(), new RuntimeBeanReference("Year" + e.getKey() + "DataSource")));
        }
    }
}
于 2012-08-27T08:06:25.410 回答
1

我可以告诉你注释方法。我会在属性文件中添加 url 和配置,并执行以下操作:

@Bean(name="dataSourceMap")
public Map<String, DataSource> dataSourceMap(DataSource dataSource2011, DataSource dataSource2012) {
    // read properties from properties file and create map of datasource

    Map<DataSource> map = new HashMap<>();
    map.put("dataSource2011",dataSource2011);
    map.put("dataSource2012",dataSource2012);
    //map.put("dataSource2013",dataSource2013);
    return map;
}

@Bean(name="dataSource2011",destroyMethod="close")
public DataSource dataSource() {
    // read properties from properties file and create map of 

    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setUrl(url2011);
    dataSource.setUsername(username2011);
    dataSource.setPassword(password2011);               
    return dataSource;
}

@Bean(name="dataSource2012",destroyMethod="close")
public DataSource dataSource() {
    // read properties from properties file and create map of 

    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setUrl(url2012);
    dataSource.setUsername(username2012);
    dataSource.setPassword(password2012);               
    return dataSource;
}
于 2012-08-23T11:33:09.827 回答