Spring Boot:Tomcat非法访问异常求助(Eclipse环境)
这个问题我之前在类似环境下也碰到过,核心原因是MySQL JDBC驱动中的AbandonedConnectionCleanupThread在Web应用停止后仍在后台运行,它试图使用已经被Tomcat销毁的Web应用类加载器加载资源,从而触发了这个非法访问异常。结合你的环境(Eclipse Mars、Tomcat 8、Java 8、Spring 5、Hibernate 5),给你几个可行的解决方案:
方案1:手动触发清理线程 shutdown
在Spring应用中添加销毁钩子,强制停止MySQL的清理线程,避免它在应用停止后继续运行。
方式A:用Spring的@PreDestroy注解
在你的配置类或者Bean中添加销毁方法:
@Configuration public class AppShutdownConfig { @PreDestroy public void cleanUpMySQLThreads() { try { // 停止MySQL的连接清理线程 com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.shutdown(); } catch (InterruptedException e) { // 恢复线程中断状态 Thread.currentThread().interrupt(); // 可以根据需要打印日志 e.printStackTrace(); } } }
方式B:用ServletContextListener
如果你的项目还在使用web.xml配置,可以写一个监听器在应用销毁时触发线程关闭:
public class MySQLConnectionCleanupListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent event) { try { com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.shutdown(); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void contextInitialized(ServletContextEvent event) { // 初始化逻辑(可选) } }
然后在web.xml中注册这个监听器:
<listener> <listener-class>com.yourpackage.MySQLConnectionCleanupListener</listener-class> </listener>
方案2:升级MySQL JDBC驱动版本
这个线程泄漏问题在较新的mysql-connector-java版本(比如8.0.20+)中已经被官方修复。如果你能升级驱动,这个异常大概率会自动消失。
注意:升级到8.x版本后,需要调整JDBC URL的参数,比如添加时区配置:
jdbc:mysql://localhost:3306/your_db?serverTimezone=UTC&useSSL=false
同时驱动类名也从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver,记得同步修改Spring或Hibernate配置中的驱动类。
方案3:Tomcat层面的临时配置
如果不想修改代码或升级驱动,可以在Tomcat的启动脚本中添加JVM参数,让清理线程自动超时终止:
- Linux/macOS:编辑
Tomcat/bin/catalina.sh,在JAVA_OPTS中添加:JAVA_OPTS="$JAVA_OPTS -Dmysql.abandoned.connection.cleanup.timeout=30" - Windows:编辑
Tomcat/bin/catalina.bat,添加:set JAVA_OPTS=%JAVA_OPTS% -Dmysql.abandoned.connection.cleanup.timeout=30
这个参数会让清理线程在30秒无活动后自动终止,避免它在Web应用停止后继续占用资源。
额外注意点
确保Hibernate的SessionFactory在应用停止时被正确关闭,未关闭的数据库连接也会导致清理线程一直运行。可以在Spring中添加销毁方法:
@Autowired private SessionFactory sessionFactory; @PreDestroy public void closeSessionFactory() { if (sessionFactory != null && !sessionFactory.isClosed()) { sessionFactory.close(); } }
内容的提问来源于stack exchange,提问作者Alna

