使用Spring + dbcp刷新DataSource

时间:2011-02-15 17:49:23

标签: java spring datasource apache-commons-dbcp

我正在使用Spring和DBCP,并且当操作环境上的某些配置发生更改时需要刷新我的数据源,而无需重新启动所有应用程序。

如果我在不使用DBCP的情况下执行此操作,则强制此刷新关闭当前正在打开的数据源并启动DataSource的新实例。

使用DBCP + Spring,我不能那样做。

有人知道是否有可能吗?

1 个答案:

答案 0 :(得分:8)

我认为普通DBCP中没有这样的支持,主要是因为数据库连接属性在应用程序的生命周期中很少发生变化。此外,当旧数据源提供的某些连接仍然打开而其他连接已经从新的(刷新的)提供服务时,您将不得不考虑转换时间。

装饰/代理方法

我建议您使用装饰器/代理设计模式编写DataSource的自定义实现。您的实现只需调用目标数据源(由DBCP创建),大多数情况下不再执行任何操作。但是,当您调用某种refresh()方法时,您的装饰器将关闭先前创建的数据源并使用全新配置创建新数据源。记住多线程!

@Service
public class RefreshableDataSource implements DataSource {

    private AtomicReference<DataSource> target = new AtomicReference<DataSource>();

    @PostConstruct
    public void refresh() {
        target.set(createDsManuallyUsingSomeExternalConfigurationSource());
    }

    @Override
    public Connection getConnection() throws SQLException {
        return target.get().getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return target.get().getConnection(username, password);
    }

    //Rest of DataSource methods

}

createDsManuallyUsingSomeExternalConfigurationSource()方法可能如下所示:

private DataSource createDsManuallyUsingSomeExternalConfigurationSource() {
    DataSource ds = new org.apache.commons.dbcp.BasicDataSource();
    ds.setDriverClassName("org.h2.Driver");
    ds.setUrl(/*New database URL*/);
    ds.setUsername(/*New username*/);
    ds.setPassword(/*New password*/);
    return ds;
}

这是Spring bean的粗略等价物:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:mem:" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>

您不能将这样的目标bean注入代理/装饰器RefreshableDataSource,因为您希望数据源配置是动态/可刷新的,而Spring只允许您注入静态属性。这意味着您有责任创建目标BasicDataSource的实例,但正如您所看到的,这并不可怕。

实际上,我还有第二个想法:Spring SpEL AFAIK允许您从XML配置中调用其他bean的方法。但这是一个非常广泛的话题。

JNDI方法

另一种方法可能是使用JNDI获取DataSource并使用热部署(它适用于JBoss及其*-ds.xml文件。