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

Spring整合Hikari时ProxyDataSource.getConnection抛出NPE异常

问题现象

将数据库连接的XML配置转为Spring Java Bean配置后,应用上下文启动后执行数据库操作抛出空指针异常,异常栈如下:

java.lang.NullPointerException: null
    at net.ttddyy.dsproxy.support.ProxyDataSource.getConnection(ProxyDataSource.java:63)
    at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:158)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:116)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:79)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:371)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:418)

调试时未发现connection实例为null,相关配置代码如下:

配置类基础定义

@Configuration
@Order(1)
@Primary
public class OLTPHikariDatasourceConfig {

    private static final String NATIVE = "native";

HikariConfig Bean定义

@Bean(name = "hikariConfigDataSource")
    public HikariConfig dataSource(@Value("${jdbc.url}") String jdbcUrl,
            @Value("${jdbc.username}") String userName,
            @Value("${jdbc.password}") String jdbcPassword,
            @Value("${jdbc.connection.type:native}") String jdbcConnectionType,
            @Value("${oltp.auto.commit:true}") boolean autoCommit,
            @Value("${c3p0.connections.min}") int minIdle,
            @Value("${c3p0.connections.max}") int maxIdle
            ){
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName("org.postgresql.Driver");
        hikariConfig.setJdbcUrl(jdbcUrl);
        hikariConfig.setUsername(userName);
        if(NATIVE.equals(jdbcConnectionType)) {
            hikariConfig.setPassword(jdbcPassword);
        } else {

        }
        hikariConfig.setPoolName("OLTP");
        hikariConfig.setMaximumPoolSize(maxIdle);
        hikariConfig.setMinimumIdle(minIdle);
        hikariConfig.setRegisterMbeans(true);
        hikariConfig.setAutoCommit(autoCommit);

        hikariConfig.setDataSourceProperties(getDataSourceProperties());
        HikariDataSource dataSource = new HikariDataSource(hikariConfig);

        return dataSource;
    }

    private Properties getDataSourceProperties(){
        Properties properties = new Properties();
        properties.setProperty("characterEncoding", "UTF-8");
        properties.setProperty("useUnicode", "yes");
        properties.setProperty("rewriteBatchedStatements", "true");
        return properties;
    }

HikariDataSource Bean定义

@Primary
    @Bean(name = "dataSourceBase", destroyMethod = "close")
    public HikariDataSource hikarDataSource(HikariConfig hikariConfigDataSource){
        HikariDataSource hikariDataSource = new HikariDataSource(hikariConfigDataSource);
        return hikariDataSource;
    }

监听器相关Bean定义

@Bean(name = "dbAccessListener")
public DBAccessListener DBAccessListener(DatoFeatureFlagService datoFeatureFlagService) {
    DBAccessListener dbAccessListener = new DBAccessListener(datoFeatureFlagService);
    return dbAccessListener;
}

@Bean(name = "queryListener")
public ChainListener queryListener(DBAccessListener dbAccessListener) {
    ChainListener queryListener = new ChainListener();
    queryListener.addListener(dbAccessListener);
    return queryListener;
}

数据源代理相关Bean定义

@Bean(name = "proxyConfigSupport")
public ProxyConfigSpringXmlSupport proxyDataSourceSupport(ChainListener queryListener) {
    ProxyConfigSpringXmlSupport proxyConfigSpringXmlSupport = new ProxyConfigSpringXmlSupport();
    proxyConfigSpringXmlSupport.setDataSourceName("dataSourceDefaultName");
    proxyConfigSpringXmlSupport.setQueryListener(queryListener);
    return proxyConfigSpringXmlSupport;
}

    @Bean(name = "proxyConfig")
    public ProxyConfig proxyConfig(ProxyConfigSpringXmlSupport proxyConfigSpringXmlSupport) {
        return proxyConfigSpringXmlSupport.create();

    }

    @Bean(name = "dataSource")
    public ProxyDataSource proxyDataSource(HikariDataSource hikariDataSource, ProxyConfig proxyConfig){
        ProxyDataSource proxyDataSource = new ProxyDataSource();
        proxyDataSource.setDataSource(hikariDataSource);
        proxyDataSource.setProxyConfig(proxyConfig);
        return proxyDataSource;
    }


    @Bean(name = "jdbcTemplate")
    public JdbcTemplate proxyDataSource(ProxyDataSource proxyDataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(proxyDataSource);
        return jdbcTemplate;
    }

}

调试截图

调试截图


问题根因及修复方案

空指针触发点在datasource-proxy的ProxyDataSource.getConnection()方法第63行,该行逻辑是从代理配置中获取拦截器链生成代理连接,拦截器链实例为null导致NPE,由3个配置错误共同引发:

  1. HikariConfig Bean逻辑错误
    hikariConfigDataSource方法声明返回HikariConfig类型,但方法内部直接创建了HikariDataSource实例返回,导致后续创建主数据源时,传入的是已经初始化完成的数据源实例而非配置对象,引发配置拷贝异常,同时额外创建了一个未被管理的数据源连接池浪费资源。
    修复方式:删除方法内new HikariDataSource(hikariConfig)的逻辑,配置完HikariConfig参数后直接返回该配置实例。
  2. @Configuration类内Bean方法重名引发CGLIB代理异常
    dataSource和jdbcTemplate两个Bean对应的方法名都叫proxyDataSource(重载方法),Spring对@Configuration类做CGLIB代理时会出现方法拦截逻辑错乱,导致ProxyDataSource实例创建时ProxyConfig属性注入失败,拦截器链未初始化。
    修复方式:将jdbcTemplate对应的Bean方法重命名为jdbcTemplate,避免和ProxyDataSource的创建方法重名。
  3. @Primary注解位置错误
    @Primary注解直接标注在配置类上,会导致该配置类下所有Bean都被标记为Primary,容易出现同类型Bean注入冲突。
    修复方式:删除类上的@Primary注解,仅在主数据源dataSourceBase的@Bean方法上保留该注解即可。

额外优化点:

  • 当前配置使用PostgreSQL驱动,但getDataSourceProperties()中设置的characterEncoding、useUnicode、rewriteBatchedStatements均为MySQL专属参数,建议删除避免驱动警告。
  • 非native连接模式的分支未设置数据库密码,需要补全对应逻辑,否则会出现数据库认证失败。

修复后的核心代码示例:

@Configuration
@Order(1)
public class OLTPHikariDatasourceConfig {

    private static final String NATIVE = "native";

    @Bean(name = "hikariConfigDataSource")
    public HikariConfig dataSource(@Value("${jdbc.url}") String jdbcUrl,
            @Value("${jdbc.username}") String userName,
            @Value("${jdbc.password}") String jdbcPassword,
            @Value("${jdbc.connection.type:native}") String jdbcConnectionType,
            @Value("${oltp.auto.commit:true}") boolean autoCommit,
            @Value("${c3p0.connections.min}") int minIdle,
            @Value("${c3p0.connections.max}") int maxIdle
            ){
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName("org.postgresql.Driver");
        hikariConfig.setJdbcUrl(jdbcUrl);
        hikariConfig.setUsername(userName);
        if(NATIVE.equals(jdbcConnectionType)) {
            hikariConfig.setPassword(jdbcPassword);
        } else {
            // 补全非native模式的密码设置逻辑
        }
        hikariConfig.setPoolName("OLTP");
        hikariConfig.setMaximumPoolSize(maxIdle);
        hikariConfig.setMinimumIdle(minIdle);
        hikariConfig.setRegisterMbeans(true);
        hikariConfig.setAutoCommit(autoCommit);
        return hikariConfig;
    }

    @Primary
    @Bean(name = "dataSourceBase", destroyMethod = "close")
    public HikariDataSource hikariDataSource(HikariConfig hikariConfigDataSource){
        return new HikariDataSource(hikariConfigDataSource);
    }

    // 其他监听器、ProxyConfig相关Bean代码保持不变

    @Bean(name = "dataSource")
    public ProxyDataSource proxyDataSource(HikariDataSource hikariDataSource, ProxyConfig proxyConfig){
        ProxyDataSource proxyDataSource = new ProxyDataSource();
        proxyDataSource.setDataSource(hikariDataSource);
        proxyDataSource.setProxyConfig(proxyConfig);
        return proxyDataSource;
    }

    // 方法重命名,避免重名
    @Bean(name = "jdbcTemplate")
    public JdbcTemplate jdbcTemplate(ProxyDataSource proxyDataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(proxyDataSource);
        return jdbcTemplate;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:48:18