Spring Boot中JNDI场景下DelegatingDataSource循环依赖与AOP适配问题
解决JNDI数据源场景下的DelegatingDataSource循环依赖与客户端上下文设置问题
我刚好碰到过类似的WildFly数据源代理+循环依赖问题,下面给你一套可靠的解决方案:
核心思路
之前的循环依赖问题,本质是你直接把DelegatingDataSource命名为dataSource,导致Spring初始化时,其他依赖dataSource的Bean和DelegatingDataSource内部依赖的原始数据源形成了闭环。我们要做的是把原始JNDI数据源和自定义包装数据源拆分成两个独立Bean,先初始化原始数据源,再用它包装出我们需要的上下文感知数据源。
第一步:配置独立的JNDI数据源Bean
先单独注册从WildFly获取的原始JNDI数据源,避免和自定义数据源混淆:
@Configuration public class JndiDataSourceConfig { @Bean(name = "jndiOriginalDataSource") public DataSource jndiOriginalDataSource() { JndiDataSourceLookup lookup = new JndiDataSourceLookup(); // 替换成你WildFly配置里的实际JNDI名称,比如java:jboss/datasources/YourAppDB return lookup.getDataSource("java:jboss/datasources/YourBusinessDB"); } }
第二步:自定义上下文感知的DelegatingDataSource子类
重写getConnection方法,在获取连接后立即设置客户端上下文,完全避开AOP的代理穿透问题:
public class ContextAwareDataSource extends DelegatingDataSource { private static final Logger logger = LoggerFactory.getLogger(ContextAwareDataSource.class); public ContextAwareDataSource(DataSource targetDataSource) { super(targetDataSource); } @Override public Connection getConnection() throws SQLException { Connection connection = super.getConnection(); this.setClientContext(connection); return connection; } @Override public Connection getConnection(String username, String password) throws SQLException { Connection connection = super.getConnection(username, password); this.setClientContext(connection); return connection; } private void setClientContext(Connection connection) { try { // 这里替换成你实际的上下文设置逻辑,比如设置客户端ID、请求ID等 // 示例:调用数据库的上下文设置存储过程,或者用setClientInfo connection.setClientInfo("request_id", RequestContextHolder.getRequestId()); logger.debug("Successfully injected client context into database connection"); } catch (SQLException e) { logger.warn("Failed to set client context, proceeding without it", e); // 根据业务需求决定是否抛出异常,一般建议仅告警不阻断 } } }
第三步:注册主数据源Bean(解决循环依赖)
把自定义的ContextAwareDataSource注册为Spring的主数据源,依赖第一步的原始JNDI数据源:
@Configuration public class DataSourceWrapperConfig { @Bean(name = "dataSource") @Primary // 标记为优先使用的数据源,确保JdbcTemplate、ORM框架等自动使用它 public DataContextAwareDataSource dataSource(@Qualifier("jndiOriginalDataSource") DataSource jndiDataSource) { return new DataContextAwareDataSource(jndiDataSource); } }
额外注意事项
- 排除Spring Boot默认数据源自动配置:如果用了Spring Boot自动配置,需要在启动类排除默认的数据源配置,避免冲突:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class YourApplication { // ... } - 线程安全的上下文获取:确保你用来获取上下文(比如请求ID、用户信息)的工具类是线程安全的,推荐用
ThreadLocal存储上下文信息。 - WildFly数据源配置验证:确认WildFly的
standalone.xml或domain.xml里的JNDI名称和代码里的完全一致,比如:<datasource jndi-name="java:jboss/datasources/YourBusinessDB" pool-name="YourBusinessDB"> <!-- 数据源配置 --> </datasource>
这个方案完全绕过了AOP代理穿透容器的问题,同时通过拆分Bean解决了循环依赖,在WildFly生产环境已经稳定运行很久了。
内容的提问来源于stack exchange,提问作者Karthikeyan
相关产品推荐
相关产品推荐

