我正在使用Spring和DBCP,并且当操作环境上的某些配置发生更改时需要刷新我的数据源,而无需重新启动所有应用程序。
如果我在不使用DBCP的情况下执行此操作,则强制此刷新关闭当前正在打开的数据源并启动DataSource的新实例。
使用DBCP + Spring,我不能那样做。
有人知道是否有可能吗?
答案 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获取DataSource
并使用热部署(它适用于JBoss及其*-ds.xml
文件。