运行时修改Spring XML Bean属性值:数据库密码自动更新适配
解决UCP数据源密码动态更新不生效的问题
问题根源
你的Spring配置中,user和password属性通过EL表达式#{DBCred.getUserName()}/#{DBCred.getPassword()}赋值,但这只会在dataSource bean初始化时执行一次。后续DBCredService更新密码后,dataSource不会主动重新获取新凭证,单纯调用UniversalConnectionPoolManager.refreshPool()也无法触发凭证的重新加载。
方案一:通过ConnectionFactoryCallback动态获取凭证(推荐)
让UCP在每次创建新连接时,从DBCredService拉取最新的用户名和密码,确保新连接始终使用有效凭证。
- 实现自定义
ConnectionFactoryCallback:
package com.test; import oracle.jdbc.pool.OracleDataSource; import oracle.ucp.jdbc.ConnectionFactoryCallback; import com.test.DBCredService; import java.sql.SQLException; import java.util.Properties; public class DynamicDBCredCallback implements ConnectionFactoryCallback<OracleDataSource> { private final DBCredService dbCred; public DynamicDBCredCallback(DBCredService dbCred) { this.dbCred = dbCred; } @Override public OracleDataSource createConnectionFactory(Properties properties) throws SQLException { OracleDataSource ods = new OracleDataSource(); ods.setURL(properties.getProperty("url")); // 每次创建连接时获取最新凭证 ods.setUser(dbCred.getUserName()); ods.setPassword(dbCred.getPassword()); return ods; } }
- 修改Spring配置,替换原有的
user/password属性,改用自定义回调:
<bean id="DBCred" class="com.test.DBCredService" /> <bean id="dynamicCredCallback" class="com.test.DynamicDBCredCallback"> <constructor-arg ref="DBCred" /> </bean> <bean class="oracle.ucp.jdbc.PoolDataSourceImpl" id="dataSource"> <property name="connectionFactoryClassName" value="oracle.jdbc.pool.OracleDataSource"/> <property name="URL" value="${db.url}" /> <!-- 移除原有的user和password配置 --> <property name="connectionFactoryCallback" ref="dynamicCredCallback" /> <!-- 按需添加其他UCP配置,比如初始连接数、最大连接数等 --> </bean>
配置完成后,UCP每次创建新连接都会调用回调获取最新密码,密码变更后,新生成的连接会自动使用新凭证。
方案二:手动更新数据源属性并刷新连接池
如果不想改动现有核心配置,可以在密码变更时,手动更新dataSource的密码属性并强制刷新连接池,同时回收旧连接。
示例代码:
import oracle.ucp.jdbc.PoolDataSourceImpl; import oracle.ucp.admin.UniversalConnectionPoolManager; import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; import org.springframework.context.ApplicationContext; // 假设已获取Spring上下文和更新后的DBCredService ApplicationContext ctx = ...; DBCredService dbCred = ctx.getBean(DBCredService.class); PoolDataSourceImpl dataSource = (PoolDataSourceImpl) ctx.getBean("dataSource"); // 更新密码 dataSource.setPassword(dbCred.getPassword()); dataSource.setUser(dbCred.getUserName()); // 回收所有空闲连接,避免旧凭证的连接继续被使用 dataSource.purgeIdleConnections(); // 刷新连接池 UniversalConnectionPoolManager ucpManager = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager(); ucpManager.refreshPool(dataSource.getPoolName());
注意事项:
- 需确保密码变更时触发此逻辑(比如通过定时任务监听密码变更事件)
- 可以配置UCP的
maxConnectionAge属性,让旧连接在指定时间后自动回收,加速新凭证的生效
内容的提问来源于stack exchange,提问作者DummyGuy
相关产品推荐
相关产品推荐

