关闭Spring Boot应用程序时未能注销DataSource JMX MBean

19

我有一个简单的Spring Boot应用程序,使用org.apache.commons.dbcp2.BasicDataSource作为dataSource bean。

数据源通过Spring Boot自动暴露为MBean。

bean声明:

@Bean
public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setUrl(dbUrl);
    dataSource.setDriverClassName(jdbcDriver);
    dataSource.setUsername(dbUserName);
    dataSource.setPassword(dbPassword);
    return dataSource;
}

一切正常。然而,关闭应用程序时出现错误。只有在运行可执行的JAR文件时才会出现此错误。使用Gradle Spring插件(gradle bootRun)时不显示此错误。

javax.management.InstanceNotFoundException: org.apache.commons.dbcp2:name=dataSource,type=BasicDataSource
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(DefaultMBeanServerInterceptor.java:1095)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.exclusiveUnregisterMBean(DefaultMBeanServerInterceptor.java:427)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.unregisterMBean(DefaultMBeanServerInterceptor.java:415)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.unregisterMBean(JmxMBeanServer.java:546)
    at org.apache.commons.dbcp2.BasicDataSource.close(BasicDataSource.java:1822)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:350)
    at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:273)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:540)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:516)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:827)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:485)
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:921)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:895)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.doClose(EmbeddedWebApplicationContext.java:152)
    at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:809)

我想知道, 1. 这个bean是如何作为JMX MBean暴露出来的? 2. 如何正确注销该MBean?


这似乎与引导程序无关。数据源本身尝试从JMX域注销但失败了。看看数据源类型文档怎么样?顺便问一下,您为什么不能使用引导程序提供的基础设施呢? - Stephane Nicoll
1
我遇到了相同的问题,无意中发现了这个。实际上这并不是一个错误——日志将这种情况报告为一个警告。至于为什么不使用引导数据源,我想猜测OP希望微调连接池,并且如果您没有在容器中运行(这将为您池化),您实际上不会从Boot中获得池化连接(SimpleDriverDataSource实际上不会给您池化连接)。我很想知道如何用DBCP2解决这个问题,但如果它只是一个在关闭时出现的警告,我不会太担心它。 - Will
这段内容来自Spring。正如@Will所说,这只是一个警告。BasicDataSource尝试注销,但已被Spring(MBeanExporter中的destroy())注销。因此,这应该不会有问题。 - mklnwt
4个回答

26

Spring试图两次关闭BasicDataSource:

  1. 当应用程序关闭时,BasicDataSource会自动关闭
  2. Spring使用默认的destroy方法来关闭DataSource,但它已经关闭了

为避免这种情况,请使用:

@Bean(destroyMethod = "")
public DataSource dataSource() 

在您的Java配置中


这解决了我的问题,在服务器关闭期间不再有堆栈跟踪。 - user2478236
完美地工作了,也解决了我的问题。 - arjuncc

1

1
BasicDataSource 继承自 BasicDataSourceMXBean,因此它会自动注册为 MBean 到 JMX 服务器上,MBean 的名称为 [org.apache.commons.dbcp2:name=dataSource,type=BasicDataSource]。当 SpringBoot 关闭时,MBeanExporter 会注销该 MBean,然后 SpringBoot 尝试销毁 BasicDataSource,并调用 close() 方法,再次注销该 MBean(BasicDataSource 捕获 JMException 并打印此警告)。这只是一个警告。如果不想打印它,可以在 SpringBoot 中禁用 JMX。
application.yml
spring:
jmx:
enabled: false

0
我曾经遇到过同样的问题。 c3p0非常好用。
如果使用spring框架 - pom.xml
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
   <groupId>com.mchange</groupId>
   <artifactId>c3p0</artifactId>
   <version>0.9.5.2</version>
</dependency>

最初使用

DataSource ds_unpooled = DataSources.unpooledDataSource(persistenceUrl,
            persistenceUsername,
            persistencePassword);
return DataSources.pooledDataSource(ds_unpooled);

但它无法处理我需要执行的负载,因此切换到以下内容

ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass( persistenceDriver ); //loads the jdbc driver
    cpds.setJdbcUrl( persistenceUrl );
    cpds.setUser(persistenceUsername);
    cpds.setPassword(persistencePassword);
    cpds.setMinPoolSize(5);
    cpds.setMaxPoolSize(50);
    cpds.setUnreturnedConnectionTimeout(1800);
    cpds.setMaxStatements(50);
    cpds.setMaxIdleTime(21600);
    cpds.setIdleConnectionTestPeriod(10800);
return cpds;

这些值来自我在网上收集的其他帖子。

根据我的经验,在相同的环境下,对于我的特定任务,运行c3p0比dbcp2 v:2.1.1更快。

希望这能有所帮助。 干杯!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接