You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

额外注意事项

  1. 排除Spring Boot默认数据源自动配置:如果用了Spring Boot自动配置,需要在启动类排除默认的数据源配置,避免冲突:
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class YourApplication {
        // ...
    }
    
  2. 线程安全的上下文获取:确保你用来获取上下文(比如请求ID、用户信息)的工具类是线程安全的,推荐用ThreadLocal存储上下文信息。
  3. WildFly数据源配置验证:确认WildFly的standalone.xml或domain.xml里的JNDI名称和代码里的完全一致,比如:
    <datasource jndi-name="java:jboss/datasources/YourBusinessDB" pool-name="YourBusinessDB">
        <!-- 数据源配置 -->
    </datasource>
    

这个方案完全绕过了AOP代理穿透容器的问题,同时通过拆分Bean解决了循环依赖,在WildFly生产环境已经稳定运行很久了。

内容的提问来源于stack exchange,提问作者Karthikeyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 18:02:49