From ae6c0a7e6484fc6261bcaedd3b712605f04a4c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 26 Sep 2025 15:27:41 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4Threads=E7=B1=BB=20=E5=B7=B2=E7=BB=8F=E4=B8=8D?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/config/ThreadPoolConfig.java | 51 +++++++++++++-- .../dromara/common/core/utils/Threads.java | 64 ------------------- .../handler/MybatisExceptionHandler.java | 54 +++++++++++++--- 3 files changed, 91 insertions(+), 78 deletions(-) delete mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java index 2c381293b..1cc3bd683 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java @@ -4,14 +4,11 @@ import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.Threads; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.core.task.VirtualThreadTaskExecutor; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.*; /** * 线程池配置 @@ -47,7 +44,7 @@ public class ThreadPoolConfig { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); - Threads.printException(r, t); + printException(r, t); } }; this.scheduledExecutorService = scheduledThreadPoolExecutor; @@ -56,15 +53,57 @@ public class ThreadPoolConfig { /** * 销毁事件 + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. */ @PreDestroy public void destroy() { try { log.info("====关闭后台任务任务线程池===="); - Threads.shutdownAndAwaitTermination(scheduledExecutorService); + ScheduledExecutorService pool = scheduledExecutorService; + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } } catch (Exception e) { log.error(e.getMessage(), e); } } + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java deleted file mode 100644 index 86eebe2d6..000000000 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.dromara.common.core.utils; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.*; - -/** - * 线程相关工具类. - * - * @author ruoyi - */ -@Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class Threads { - - /** - * 停止线程池 - * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. - * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. - * 如果仍然超時,則強制退出. - * 另对在shutdown时线程本身被调用中断做了处理. - */ - public static void shutdownAndAwaitTermination(ExecutorService pool) { - if (pool != null && !pool.isShutdown()) { - pool.shutdown(); - try { - if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { - pool.shutdownNow(); - if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { - log.info("Pool did not terminate"); - } - } - } catch (InterruptedException ie) { - pool.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - } - - /** - * 打印线程异常信息 - */ - public static void printException(Runnable r, Throwable t) { - if (t == null && r instanceof Future) { - try { - Future future = (Future) r; - if (future.isDone()) { - future.get(); - } - } catch (CancellationException ce) { - t = ce; - } catch (ExecutionException ee) { - t = ee.getCause(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - } - if (t != null) { - log.error(t.getMessage(), t); - } - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index d33f63f3b..094785bd7 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -1,10 +1,11 @@ package org.dromara.common.mybatis.handler; +import cn.dev33.satoken.exception.NotLoginException; import cn.hutool.http.HttpStatus; +import com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; -import org.dromara.common.core.utils.StringUtils; import org.mybatis.spring.MyBatisSystemException; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -35,17 +36,54 @@ public class MybatisExceptionHandler { @ExceptionHandler(MyBatisSystemException.class) public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); - String message = e.getMessage(); - if (StringUtils.contains(message, "CannotFindDataSourceException")) { + Throwable root = getRootCause(e); + if (root instanceof NotLoginException) { + log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, root.getMessage()); + return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); + } + if (root instanceof CannotFindDataSourceException) { log.error("请求地址'{}', 未找到数据源", requestURI); return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认"); } - if (StringUtils.contains(message, "NotLoginException")) { - log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage()); - return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); - } log.error("请求地址'{}', Mybatis系统异常", requestURI, e); - return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, message); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()); + } + + /** + * 获取异常的根因(递归查找) + * + * @param e 当前异常 + * @return 根因异常(最底层的 cause) + *

+ * 逻辑说明: + * 1. 如果 e 没有 cause,说明 e 本身就是根因,直接返回 + * 2. 如果 e 的 cause 和自身相同(防止循环引用),也返回 e + * 3. 否则递归调用,继续向下寻找最底层的 cause + */ + public static Throwable getRootCause(Throwable e) { + Throwable cause = e.getCause(); + if (cause == null || cause == e) { + return e; + } + return getRootCause(cause); + } + + /** + * 在异常链中查找指定类型的异常 + * + * @param e 当前异常 + * @param clazz 目标异常类 + * @return 找到的指定类型异常,如果没有找到返回 null + */ + public static Throwable findCause(Throwable e, Class clazz) { + Throwable t = e; + while (t != null && t != t.getCause()) { + if (clazz.isInstance(t)) { + return t; + } + t = t.getCause(); + } + return null; } }