Rude Boy - Spring Boot
type: channel
link: https://rudeboy.top/index.php/tag/spring-boot/
items: 深入解析 Spring Boot 线程模型：从“服务假死”到虚拟线程的救赎
items: Java 25虚拟线程解析：@Async注解的正确打开方式
items: Spring Boot 接口限流全攻略
items: @NotNull 注解失效原因
items: 限流、熔断与降级：核心原理 + 实战落地
items: SpringBoot 使用 Spring Events 实现异步操作日志记录
items: SpringBoot中6种拦截器使用场景
items: 优雅实现 SpringBoot 并行任务指南
items: EasyExcel根据自定义模板导出Excel(包含图片、表格)
items: 升级springboo自带的第三方jar包

深入解析 Spring Boot 线程模型：从“服务假死”到虚拟线程的救赎
type: item
date: 2026-04-03T15:45:00+08:00
description: 🧵 深入解析 Spring Boot 线程模型：从“服务假死”到虚拟线程的救赎📌 引言：一场典型的“低负载”瘫痪你是否在 Spring Boot 运维中遇到过这种诡异场景？服务监控一片“健康”，​CPU 利用率仅 3%​，内存、磁盘、网络均未触及瓶颈。然而，客户端请求持续超时，如同服务已“死亡”。这并非灵异事件，而是一种在采用传统同步阻塞模型的 Spring Boot 服务中​并不罕见的性能瘫痪​。其根源并非资源耗尽，而是​所有的工作线程都被“卡”在了等待之上​——等待数据库、等待第三方 API、等待 Redis 响应。本文将从 Tomcat 的线程模型入手，深入剖析这一现象的本质，并揭示 Java 21 虚拟线程​ 如何从架构层面优雅地解决这一经典难题。1. 🔍 核心机制：Tomcat 的三层线程模型一个 HTTP 请求在 Spring Boot（默认使用 Tomcat）中的旅程，远非“一个请求创建一个线程”那么简单，而是被一个精巧的三层模型高效处理：线程类型数量 (默认)职责关键行为Acceptor​1 个接受新连接从操作系统的 TCPbacklog队列中accept()新连接，并转交给 Poller。Poller​核心数监听 IO 事件使用 NIOSelector监听已接收连接的读写事件。​数据未就绪时，不占用工作线程​。Worker​最多 200 个执行业务逻辑从线程池被唤醒，独占式处理请求：解析 HTTP、执行 Filter 和 Controller、访问数据库、返回响应。​关键配置​：server.tomcat.threads.max=200：控制 ​Worker 线程池的最大数量​，这是并发处理能力的硬上限。server.tomcat.accept-count=100：当所有 Worker 忙碌时，请求的排队队列长度。队列也满，则直接拒绝连接。​核心结论​：准确地说，是 ​“一个正在处理的请求，在其整个生命周期内独占一个 Worker 线程”​。Acceptor 和 Poller 是共享的，高效且轻量。2. 💥 瘫痪根源：当线程在等待中被“绑架”理解了 Worker 线程的独占性，问题就显而易见了：线程在等待 IO 时虽然自身不消耗 CPU，但它占用的“座位”并不释放。典型阻塞场景与后果场景Worker 线程状态对服务的影响执行一个 2 秒的慢 SQL​在socketRead上等待数据库响应，持续 2 秒。该线程 2 秒内无法处理任何新请求。调用 5 秒才响应的支付接口​在 HTTP 客户端上等待，持续 5 秒。同上，线程被长期占用。​连锁反应​：瞬时涌入 200 个上述慢请求200 个 Worker 线程全部进入等待状态。​线程池耗尽​。新请求在队列中堆积，队列满后直接被拒绝，​服务对外表现为完全不可用​，而 CPU 使用率却极低。​传统同步阻塞模型的根本缺陷​：​应用的整体并发能力，不取决于 CPU 或内存，而取决于（线程数 × 线程平均等待时间）。​​ 通过 jstack命令，你可以清晰地看到大量线程卡在 RUNNABLE状态，但堆栈信息显示它们正阻塞在各类 IO 调用上。3. ⚠️ 常见的“曲线救国”与陷阱：@Async为了不阻塞 Worker 线程，开发者常使用 @Async将耗时任务异步化。@GetMapping("/order") public Result createOrder() { orderService.createOrder(); // 同步，占用 Tomcat Worker 线程 notifyService.sendNotification(); // @Async，提交到其他线程池 return Result.success(); // Worker 线程立即释放 }​需要注意的陷阱​：Spring Boot 为 @Async默认配置的 ThreadPoolTaskExecutor核心属性如下：corePoolSize: 8queueCapacity: Integer.MAX_VALUE（无限队列）maxPoolSize: Integer.MAX_VALUE由于 ThreadPoolExecutor的扩容规则是“​队列满了才会创建超出核心数的线程​”，这意味着异步任务会无限堆积在队列中，永远只有 8 个核心线程在处理。这可能导致：任务响应时间极长。队列堆积消耗大量内存，存在 OOM 风险。​生产环境建议​：务必显式配置异步线程池的边界。spring: task: execution: pool: core-size: 8 max-size: 50 queue-capacity: 10004. 🚀 革命性解决方案：Java 21 虚拟线程Java 21 引入的虚拟线程（Virtual…

Java 25虚拟线程解析：@Async注解的正确打开方式
type: item
date: 2025-10-25T15:43:32+08:00
description: Java 25虚拟线程解析：@Async注解的正确打开方式现代Spring应用程序的难题现代Spring应用程序都面临着同样的难题：​如何在不消耗过多CPU资源或阻塞线程的情况下，快速运行大量任务？​​并发处理核心概念提到并发处理，我们经常听到三个概念：​​虚拟线程​、线程池​和​​@Async注解​。很多开发者容易将它们混淆，认为它们是同一件事。​其实不然！​​​虚拟线程（Virtual Threads）​​ - JVM的一种执行模型​线程池（Thread Pools）​​ - 资源管理器​​@Async注解​ - Spring的任务路由工具，将工作分发给你选择的执行器💡 理解它们的区别对于应用的延迟、云成本和可靠性至关重要。选择错误可能导致P95延迟飙升、队列堆积和超时问题。什么是虚拟线程？虚拟线程在阻塞I/O操作时能够​快速地暂停和恢复​。核心优势✅ ​高并发​：可以创建数百万个虚拟线程✅ ​低成本​：内存占用极小（约8KB vs 传统线程的2MB）✅ ​高效I/O​：在I/O阻塞时自动让出CPU资源适用场景示例// 适合：大量I/O密集型任务 @Async public CompletableFuture<String> fetchDataFromAPI(String url) { // 网络请求、数据库查询等 return restTemplate.getForObject(url, String.class); }线程池详解线程池是一种​资源管理策略​，通过预先创建固定数量的线程来处理任务，避免频繁创建和销毁线程的开销。标准配置示例@Configuration public class AsyncConfig { @Bean(name = "taskExecutor") public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(50); executor.setQueueCapacity(100); executor.setThreadNamePrefix("async-task-"); executor.initialize(); return executor; } }@Async注解的作用机制@Async是Spring提供的​任务路由器​，它将方法调用转发给指定的执行器处理。基础用法@Service public class UserService { @Async("taskExecutor") public CompletableFuture<User> processUser(Long userId) { // 异步处理逻辑 User user = userRepository.findById(userId); // 耗时操作... return CompletableFuture.completedFuture(user); } }虚拟线程执行器配置Spring Boot 3.5+ 配置要求组件最低版本说明Java21+虚拟线程正式版本Spring Boot3.2+完整支持虚拟线程配置Spring Framework6.1+底层框架支持完整配置代码@Configuration @EnableAsync public class VirtualThreadConfig { @Bean(name = "virtualThreadExecutor") public TaskExecutor virtualThreadExecutor() { return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor()); } @Bean(name = "fixedThreadPool") public TaskExecutor fixedThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20); executor.setMaxPoolSize(40); executor.setQueueCapacity(200); executor.setThreadNamePrefix("fixed-pool-"); executor.initialize(); return executor; } }服务层使用示例@Service public…

Spring Boot 接口限流全攻略
type: item
date: 2025-07-13T15:35:00+08:00
description: 🛡️ Spring Boot 接口限流全攻略注解 + 令牌桶 + 用户 / IP / 设备维度限流一网打尽！高并发下，服务不怕流量大，怕的是毫无防线地“裸奔”。限流机制正是系统的第一道防火墙——它不是增长的绊脚石，而是保障业务可持续运行的关键护盾。本文将带你从 注解 + AOP 切入，构建一套支持 用户、IP、设备ID 多维度、可组合的 客体级限流系统。核心基于 Guava RateLimiter 实现令牌桶算法，以高性能、非侵入、易配置的方式守护你的接口安全。📁 1. 目录结构与包规划src/main/java/com/icoderoad/ ├── annotation/ 📌 限流注解定义 ├── aspect/ 🎯 AOP 限流切面 ├── controller/ 🌐 控制器示例 ├── exception/ ❗ 自定义异常 ├── handler/ 🛠️ 全局异常处理 └── utils/ 🔧 工具类（用户/IP/设备信息提取）🧠 2. 限流场景与算法回顾限流的本质：防止系统资源被过度消耗，保证整体稳定性。2.1 常见限流维度维度说明适用场景接口级别限制某个接口的总访问频率全局流量控制用户级别控制同一用户的请求频率防单一用户刷接口IP 级别同一 IP 在单位时间内的请求数限制防爬虫、DDoS设备 ID 级别基于设备号或终端号限流移动端防刷2.2 常用限流算法固定窗口算法滑动窗口算法漏桶算法令牌桶算法 ✅（本文采用，由 Guava RateLimiter 实现）🚀 令牌桶算法的优势：允许一定的突发流量，平滑处理请求，同时保证平均速率。💻 3. 核心实现（多维度组合限流）🏷️ 3.1 注解定义路径： com.icoderoad.annotation.RateLimit@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimit { /** 每 time 秒允许的最大请求次数 */ int count() default 100; /** 时间窗口长度（秒） */ int time() default 1; /** 是否按用户维度限流 */ boolean perUser() default false; /** 是否按IP维度限流 */ boolean perIp() default false; /** 是否按设备ID维度限流 */ boolean perDevice() default false; }💡 语义说明当同时启用多个维度（如 perUser=true, perIp=true）时，生成的 key 会将所有维度组合为一个整体（例如 method:USER:123:IP:10.0.0.1），即 所有条件同时满足 才会共享同一个令牌桶。若需 各维度独立限流（如用户限流 5次/秒 同时 IP 限流 10次/秒），需在切面中多次校验，或使用多个注解。下文控制器部分会给出简单演示。🔧 3.2 工具类：请求维度信息抽取路径： com.icoderoad.utils.RequestUtilspublic class RequestUtils { public static HttpServletRequest getRequest() { RequestAttributes attr = RequestContextHolder.getRequestAttributes(); return attr == null ? null : ((ServletRequestAttributes) attr).getRequest(); } /** 从安全上下文中获取当前用户ID（示例固定值，生产需集成JWT/Session） */ public static String getUserId() { // TODO: 实际项目应从 SecurityContextHolder 或 Token 中解析 return "user-123"; } /** 获取客户端真实IP（穿透代理） */ public static String getIpAddr() { HttpServletRequest request = getRequest(); if (request == null) return "unknown"; String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP")…

@NotNull 注解失效原因
type: item
date: 2025-07-12T21:06:57+08:00
description: @NotNull 注解失效原因​​现象​​：给 String 类型添加 @NotNull 注解后，仅当参数 ​​未传入（值为 null）​​ 时会触发异常；若传入 ​​空字符串 ""​​ 则不会报错，导致预期外的校验失效。解决方案注解的正确使用场景：注解适用场景示例说明@NotNull​​基本类型​​必须存在且不能为null@NotEmpty​​集合类​​集合不能为null或空集合@NotBlank​​字符串类型​​字符串不能为null、空或纯空格关键步骤：在 Controller 的接口参数前添加 ​​@Validated​​ 注解激活校验：public Result<?> createUser(@Validated @RequestBody UserDTO user) { // 业务逻辑 }总结​​@NotNull​​：仅校验 null，不拦截空字符串 ""。​​@NotBlank​​：校验字符串非 null、非空、非空格。​​@Validated​​：​​必须添加​​在参数前以触发校验规则。作者：时生说明：一个正在努力的小白。欢迎批评、指正、参考。

限流、熔断与降级：核心原理 + 实战落地
type: item
date: 2025-07-05T10:35:00+08:00
description: 📊 限流、熔断与降级：核心原理 + 实战落地在高并发系统中，限流、熔断、降级是保障系统稳定性的三大核心能力。它们本质上属于系统的“自我保护机制”，目标都是：防止系统在流量洪峰或服务异常时被拖垮。很多人会背概念，但真正能打动面试官的，是：你是否理解底层原理是否做过真实项目实践是否踩过坑并解决问题是否量化过优化效果下面从 原理 → 算法 → 项目实践 → 技术实现 → 优化方案 五个维度，系统梳理。一、限流、熔断、降级的核心区别🚦1. 限流（Rate Limiting）限流的核心目标是：控制进入系统的请求数量，避免瞬时流量压垮服务。它属于一种“前置保护”。比如系统每秒最多只能处理 2000 个请求，当流量突然达到 1w QPS 时：超过阈值的请求直接拒绝或进入排队等待从而保证系统核心能力不崩溃典型场景秒杀系统API 网关登录接口防刷、防爬热点活动⚡2. 熔断（Circuit Breaker）熔断的本质：当下游服务异常时，快速失败，避免故障扩散。类似于“保险丝”。例如：用户服务调用订单服务订单服务响应超时如果继续不断重试会导致线程池耗尽最终整个系统雪崩因此熔断器会：检测失败率达到阈值后直接熔断暂时不再请求下游一段时间后进入“半开状态”尝试恢复🛡️3. 降级（Fallback）降级本质是：系统在资源不足或异常情况下，优先保证核心功能。它强调的是：“有损服务”“体验让步”“核心链路优先”例如：场景降级方案推荐服务异常返回默认推荐评论服务挂掉隐藏评论区用户画像失败使用缓存数据大促期间关闭非核心功能二、三者的关系与职责边界很多人容易混淆，这里一句话总结：技术核心目标作用位置限流控制流量系统入口熔断防止扩散服务调用层降级保底兜底业务层它们通常是组合使用的：流量进来 ↓ 限流保护系统 ↓ 调用下游服务 ↓ 熔断避免雪崩 ↓ 降级返回兜底数据这是高可用系统最经典的防护链路。三、常见限流算法详解（面试重点）1️⃣ 固定窗口算法（Fixed Window）原理：固定时间窗口统计请求数超过阈值则拒绝例如：1 秒内最多 100 次请求优点：实现简单性能高缺点：存在“临界问题”例如：00:00:59 → 100 次 00:01:00 → 100 次两秒内可能瞬间达到 200 次。适用于：简单业务低频接口2️⃣ 滑动窗口算法（Sliding Window）改进点：将大窗口拆成多个小窗口统计。例如：10 个 100ms 小窗口优点：流量更加平滑缓解突刺问题缺点：实现复杂度提高统计成本更高适用于：API 网关微服务接口3️⃣ 漏桶算法（Leaky Bucket）核心思想：请求先进桶，再以固定速率流出。特点：强制匀速处理削峰填谷缺点：无法很好应对突发流量适用于：消息队列流量整形网络传输4️⃣ 令牌桶算法（Token Bucket）目前最常用。核心思想：系统按固定速率生成 Token请求拿到 Token 才能执行优点：允许一定突发流量更适合互联网高并发这是：SentinelGuava RateLimiterNginx limit\_req最核心的实现思想。四、项目中的真实落地方案（重点）这里是面试最加分的部分。🏗️ 项目架构：分层防护体系实际生产中，一般不会只做单层防护。而是：网关层限流 ↓ 服务层熔断 ↓ 业务层降级 ↓ 缓存 + MQ 削峰形成完整高可用体系。五、网关层实践（Spring Cloud Gateway）网关是第一道防线。通常会做：🚪1. API 限流例如：用户维度IP 维度接口维度租户维度限制 QPS。常见实现Redis + LuaSentinel GatewayNginx limit\_reqKong/APISIX🔥2. 热点接口保护例如：秒杀接口 商品详情接口 登录接口需要重点保护。通常：单独配置阈值热点参数限流动态规则调整🧩3. 非核心服务自动降级例如：推荐服务Banner 服务埋点服务异常时直接降级：{ "msg": "系统繁忙" }优先保障：下单支付库存核心链路。六、服务层熔断实践（Sentinel）目前国内最常见的是：SentinelResilience4jHystrix（已停止维护）其中 Sentinel 在阿里系应用最广。七、Sentinel 核心能力1️⃣ 流控规则支持：QPS并发线程数关联限流链路限流2️⃣ 熔断规则支持：慢调用比例异常比例异常数达到阈值后：Closed → Open → Half Open完成熔断恢复。3️⃣ 热点参数限流这是 Sentinel 非常强的能力。例如：@SentinelResource( value = "queryOrder", blockHandler = "handleBlock" ) public Order queryOrder(String orderId) { return orde…

SpringBoot 使用 Spring Events 实现异步操作日志记录
type: item
date: 2025-06-19T19:35:56+08:00
description: 🌟 SpringBoot 使用 Spring Events 实现异步操作日志记录🚀 引言在 SpringBoot 应用中，通过 Spring Events 实现异步记录操作日志，可以将日志记录任务与主业务流程解耦，​​提升系统性能​，​​避免同步阻塞​，同时保持代码的​简洁性和可维护性​。本文将详细介绍完整实现方案。📚 目录结构1. 📌 自定义操作日志记录注解 2. 🧩 日志定义枚举 3. 📦 操作日志事件实体 4. 🔍 操作日志记录处理切面 5. 🧰 系统工具类 6. 🔊 异步日志记录处理监听 7. 🗃️ 日志表结构 8. 🎯 控制层使用示例 9. 💎 技术优势 10. 🌈 相关项目推荐1. 📌 自定义操作日志记录注解package com.demo.annotation; import com.demo.constant.LogEnum; import java.lang.annotation.*; /** * 🎯 自定义操作日志记录注解 * * @description 用于标记需要记录操作日志的方法 */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** * 业务模块名称 */ String module() default ""; /** * 操作类型（默认其他操作） */ LogEnum businessType() default LogEnum.OTHER; /** * 操作描述 */ String desc() default ""; /** * 操作人员类别（默认后台用户） */ LogEnum operatorType() default LogEnum.MANAGE; }2. 🧩 日志定义枚举package com.demo.constant; import lombok.AllArgsConstructor; import lombok.Getter; /** * 🧾 日志定义枚举 * * @description 定义操作类型和操作人员类别 */ @Getter @AllArgsConstructor public enum LogEnum { /* 业务操作类型 */ SELECT("SELECT", "查询"), INSERT("INSERT", "新增"), UPDATE("UPDATE", "修改"), DELETE("DELETE", "删除"), GRANT("GRANT", "授权"), EXPORT("EXPORT", "导出"), IMPORT("IMPORT", "导入"), FORCE("FORCE", "强退"), CLEAN("CLEAN", "清空数据"), OTHER("OTHER", "其他"), /* 操作人员类别 */ UNKNOWN("UNKNOWN", "未知用户"), MANAGE("MANAGE", "后台用户"), MOBILE("MOBILE", "移动端用户"); private final String key; private final String value; }3. 📦 操作日志事件实体package com.demo.event; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 📝 操作日志事件实体 * * @description 封装操作日志的详细信息 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class OperLogEvent implements Serializable { private static final long serialVersionUID = 1L; // 功能模块 private String module; // 类名+方法名 private String method; // 操作类型（SELECT/INSERT等） private String type; // 操作描述 private String message; // 请求参数（JSON格式） private String reqParam; // 响应参数（JSON格式） private String resPa…

SpringBoot中6种拦截器使用场景
type: item
date: 2025-06-17T19:53:03+08:00
description: SpringBoot中6种拦截器使用场景在构建企业级Web应用时，我们需要在请求处理的不同阶段执行通用逻辑（如权限验证、日志记录等）。Spring MVC的拦截器机制提供了一种优雅的实现方式。本文将详细介绍6种常见拦截器使用场景及其实现方案。拦截器基础什么是拦截器？拦截器是Spring MVC提供的一种机制，用于在控制器处理请求前后执行特定逻辑。拦截器 vs 过滤器维度拦截器(Interceptor)过滤器(Filter)​归属​Spring框架Servlet规范​拦截范围​Spring MVC请求所有请求​执行顺序​在过滤器之后执行在拦截器之前执行拦截器生命周期方法public interface HandlerInterceptor { boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler); void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView); void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex); }场景一：用户认证拦截器使用场景✅ 验证用户登录状态✅ 检查资源访问权限✅ 实现JWT token验证核心实现@Component public class AuthenticationInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 检查@PermitAll注解 PermitAll permitAll = handlerMethod.getMethodAnnotation(PermitAll.class); if (permitAll != null) return true; // 2. 验证Token有效性 String token = request.getHeader("Authorization"); if (!jwtTokenProvider.validateToken(token)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } // 3. 角色权限检查 RequireRole requireRole = handlerMethod.getMethodAnnotation(RequireRole.class); if (requireRole != null && !user.hasRole(requireRole.value())) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return false; } // 4. 用户信息存储 request.setAttribute("currentUser", user); return true; } }自定义注解@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PermitAll {} @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { String[] value(); }最佳实践使用注解标记需认证的接口为不同安全级别API设计路径前缀拦截器逻辑抽取到独立服务类添加详细日志便于问题排查场景二：日志记录拦截器使用场景📝 记录API请求/响应内容👣 跟踪用户行为📊 收集系统使用统计🔍 辅助问题排查核心实现@Slf4j @Component public class LoggingInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletR…

优雅实现 SpringBoot 并行任务指南
type: item
date: 2025-06-11T10:31:00+08:00
description: 🌟 优雅实现 SpringBoot 并行任务指南📌 引言：定时任务的常见场景在日常开发中，我们经常遇到各种定时任务需求：⏰ ​​报表清理​​：每天凌晨执行❤️ ​​心跳检测​​：每隔10分钟执行🔄 ​​数据同步​​：定时从外部系统拉取数据Spring Boot 的 @Scheduled 注解提供了便捷的定时任务支持，但默认情况下存在一个关键问题：​​你以为任务在并行执行，实际上它们是串行排队运行的！​​🚀 基础定时任务实现简单示例代码@Component public class MyTask { // 固定延迟：上次执行完成后5秒再执行 @Scheduled(fixedDelay = 5000) public void task1() { System.out.println("任务1执行：" + LocalDateTime.now()); } // Cron表达式：每10秒执行一次 @Scheduled(cron = "0/10 * * * * ?") public void task2() { System.out.println("任务2执行：" + LocalDateTime.now()); } }启用定时任务只需在配置类添加 @EnableScheduling 注解⚠️ 隐藏问题：单线程陷阱Spring Boot 默认使用单线程执行所有定时任务：// 源码实现 this.localExecutor = Executors.newSingleThreadScheduledExecutor();这会导致：长任务阻塞其他任务执行任务异常导致整个定时任务系统崩溃无法充分利用多核CPU资源🛠️ 并行任务解决方案核心配置：自定义线程池@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar registrar) { // 创建10个线程的调度线程池 registrar.setScheduler(Executors.newScheduledThreadPool(10)); } }线程池配置指南任务类型建议线程数说明轻量任务5-10HTTP请求/简单DB操作IO密集型任务20+文件处理/网络通信CPU密集型任务CPU核心数复杂计算/数据处理💡 高级配置可使用 ThreadPoolTaskScheduler 定制线程名/异常处理等@Scheduled 参数详解参数类型执行逻辑使用场景fixedRate固定频率（从上一次开始计算）需要严格周期执行的任务fixedDelay固定延迟（从上次完成计算）需要保证执行间隔的任务initialDelay首次延迟后按固定频率执行避免应用启动立即执行cron使用Cron表达式定制执行时间复杂时间调度需求🧩 多线程环境注意事项并发问题与解决方案问题类型风险描述解决方案​​线程安全​​共享资源竞争使用锁机制(synchronized/ReentrantLock)​​异常处理​​线程池吞没异常添加UncaughtExceptionHandler或try-catch​​资源控制​​线程耗尽导致系统崩溃合理设置线程数/队列容量/熔断机制​​状态监控​​任务失败无感知添加日志监控+报警通知机制最佳实践建议​​资源隔离​​：不同业务使用独立线程池​​超时控制​​：为任务添加执行超时限制​​优雅关闭​​：实现DisposableBean确保任务安全停止​​幂等设计​​：保证任务可重复执行不产生副作用🏗️ 任务调度架构选型轻量级 vs 专业级方案​​方案​​​​适用场景​​​​优势​​​​推荐框架​​​​Spring内置​​简单任务/内部系统零依赖/快速集成@Scheduled​​专业调度​​复杂业务/分布式环境高可靠/集群支持Quartz/XXL-JOB​​高性能​​海量任务/分片处理横向扩展/负载均衡Elastic-Job专业框架对比框架分布式支持任务分片可视化界面学习曲线​​Quartz​​✅✅❌中等​​Elastic-Job​​✅✅✅较陡峭​​XXL-JOB​​✅✅✅平缓⚠️ ​​重要提示​​：切勿使用定时任务替代消息队列！高频任务(>100TPS)请使用MQ解决方案💎 总结与最佳实践核心要点​​默认单线程​​：Spring Boot定时任务默认串行执行​​线程池配置​​：通过SchedulingConfigurer实现并行执行​​合理规划​​：根据任务类型设置合适线程数​​防护措施​​：添加异常处理+资源控制+监控报警架构选择建议graph TD A["任务需求分析"] --> B{"任务复杂度…

EasyExcel根据自定义模板导出Excel(包含图片、表格)
type: item
date: 2025-05-03T21:31:00+08:00
description: EasyExcel 模板导出指南：图片与表格处理详解前言使用 EasyExcel 3.0.2+ 版本实现模板导出：<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.0.2</version> </dependency>核心难点：将图片精准插入到模板指定位置一、Controller 层实现@ApiOperation("导出Excel") @PostMapping("/export") public void exportExcel(@RequestParam("file") MultipartFile file, HttpServletResponse response, @Valid FwFloodForecastResultExportDto dto) { service.export(file, response, dto); }二、Service 实现层（优化版）public void export(MultipartFile file, HttpServletResponse response, FwFloodForecastResultExportDto dto) { // 1. 准备模板数据 Map<String, Object> map = new HashMap<>(); map.put("startW", 33); map.put("avgRain", 35); // ...其他字段... map.put("maxWTime", "11月27日20时"); // 2. 动态创建表格数据（优化版） List<Map<String, Object>> tableData = createTableData(); // 3. 处理图片和导出 try (InputStream is = ResourceUtil.getStream("classpath:templates/forecastTemp.xlsx")) { String fileName = "洪水预报结果_" + LocalDate.now() + ".xlsx"; // 将图片转换为Excel单元格数据 WriteCellData<Void> imageCell = TemplateExcelUtils.createImageCell(file.getBytes()); map.put("img", imageCell); // "img"对应模板中的占位符{img} ExcelWriter excelWriter = EasyExcel.write(getOutputStream(fileName, response)) .withTemplate(is) .excelType(ExcelTypeEnum.XLSX) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); // 填充普通数据 excelWriter.fill(map, writeSheet); // 填充表格数据（"data"为模板中的表格占位符） excelWriter.fill(new FillWrapper("data", tableData), writeSheet); excelWriter.finish(); } catch (Exception e) { log.error("导出Excel失败", e); throw new RuntimeException("导出失败"); } } // 动态创建表格数据 private List<Map<String, Object>> createTableData() { String[] months = {"11", "12", "1", "2", "3"}; List<Map<String, Object>> list = new ArrayList<>(); for (String month : months) { Map<String, Object> row = new HashMap<>(); row.put("month", month); row.put("day", "3"); row.put("hour", "10"); row.put("rain", 40); row.put("inq", 41); row.put("otq", 42); list.add(row); } return list; }三、图片处理工具类public class TemplateExcelUtils { /** * …

升级springboo自带的第三方jar包
type: item
date: 2023-05-24T16:52:00+08:00
description: 升级原因：有些第三方jar存在安全问题，又不能升级是springboot版本，所以只能单独升级springboo自带的第三方jar包例子说明：以升级logback-classic这个包解决办法：排除springboot自带的logback-classic包，再添加升级之后的版本（如果是其他第三包中包含logback-classic包也可以这样做）找到springboot项目中所有如下代码 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-xxx</artifactId> </dependency> 修改为（去除logback-classic依赖，再新增新的需要的依赖） <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-xxx</artifactId> <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency> 全局替换(spring专用)，自定义spring-boot-dependencies中logback-classic版本号在spring-boot-dependencies内查看logback-classic使用的版本 <logback.version>1.2.3</logback.version> pom文件中properties标签中添加需要升级的版本<properties> <logback.version>1.2.11</logback.version> </properties>