03 November 2019

一般情况下,SpringBoot中使用Hikari只需要在yaml文件中配置Hikari的数据源信息即可,例如

spring:
    datasource:
        url: jdbc:mysql://localhost:3306/test
        username: foo
        password: bar
        hikari:
            connection-timeout: 30000
            minimum-idle: 5
            maximum-pool-size: 12
            idle-timeout: 300000
            max-lifetime: 1200000
            register-mbeans: true

不过,若是你需要手动创建数据源Bean,则很有可能会得到如下错误

org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [HikariDataSource (HikariDataSource)] with key 'dataSource'; nested exception is javax.management.InstanceAlreadyExistsException: MXBean already registered with name com.zaxxer.hikari:type=PoolConfig (HikariDataSource)

错误的原因是,Hikari数据源的创建方法里自带了注册MBean的操作,而SpringBoot在初始化时会搜索所有Bean,并将可能是MBean的Bean注册为MBean(判断方法参见org.springframework.jmx.support.JmxUtils#isMBean方法,大意是判断这个类是否继承自javax.management.DynamicMBean,或者该类继承/实现了以某个”MBean”和或”MXBean”结尾的类/接口),从而导致重复注册MBean的错误。

解决方法是,将这个手动创建的Bean放到org.springframework.jmx.export.MBeanExporter的排除列表里,如下所示:

@Configuration
public class BeansConfig {
    @Resource
    private HikariDataSourceConfig hikariDataSourceConfig;
    @Resource
    private ObjectProvider<MBeanExporter> mBeanExporter;
    @Bean("dataSource")
    public DataSource createDataSource() {
        String url = hikariDataSourceConfig.getUrl();
        String username = hikariDataSourceConfig.getUsername();
        String password = hikariDataSourceConfig.getPassword();
        long idleTimeoutInMilliSeconds =
                hikariDataSourceConfig.getIdleTimeOutInMilliseconds();
        long maxLifetimeInMilliseconds =
                hikariDataSourceConfig.getMaxLifetimeInMilliseconds();
        int maximumPoolSize = hikariDataSourceConfig.getMaximumPoolSize();
        int minimumIdle = hikariDataSourceConfig.getMinimumIdle();
        String poolName = "HikariDataSource";
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setRegisterMbeans(true);
        hikariConfig.setJdbcUrl(url);
        hikariConfig.setUsername(username);
        hikariConfig.setPassword(password);
        hikariConfig.setIdleTimeout(idleTimeoutInMilliSeconds);
        hikariConfig.setMaxLifetime(maxLifetimeInMilliseconds);
        hikariConfig.setMaximumPoolSize(maximumPoolSize);
        hikariConfig.setMinimumIdle(minimumIdle);
        hikariConfig.setPoolName(poolName);
        HikariDataSource dataSource = new HikariDataSource(hikariConfig);
        mBeanExporter
                .ifUnique((exporter) -> exporter.addExcludedBean("dataSource"));
        return dataSource;
    }
}

这里指的注意的是,Bean的名字”dataSource”通过mBeanExporter.ifUnique((exporter) -> exporter.addExcludedBean("dataSource"));放入到排除列表里,这样就不会出现重复注册的问题了。