Spring单例Bean线程安全问题:原理、排查与五大解决实践
做了这么多年Java后端每次线上出了诡异问题排查到最后一层十有八九都绕不开Spring单例线程安全。这个点既是面试必考题也是生产环境最常见的坑之一。Spring默认把Bean设计成单例而单例意味着全局共享一旦共享的数据被多个线程同时读写线程安全问题就跟着来了。这篇文章我会把自己这些年踩过的坑、排查的思路、落地的解决方案全部拆开讲清楚看完你至少能知道什么情况下单例Bean会不安全、出问题时怎么定位、日常写代码怎么提前规避。我默认你已经有Spring基础但下面所有内容我都会从原理到代码逐步展开新手也不用担心看不懂。1. 为什么Spring默认单例又为什么会扯上线程安全1.1 默认单例不是“性能优化”那么简单它是一种设计约定很多人一谈单例第一反应是“Spring为了减少对象创建开销所以默认单例”。这话对但不完整。Spring容器中默认的Scope就是singleton容器启动时创建Bean并把实例缓存起来后续的注入和getBean拿到的都是同一个对象。它的好处不只是少new几次更核心的是让整个容器内的依赖关系变得可预期同一个Service不管谁注入拿到的都是同一份配置、状态、连接都能统一管理。真正在工程里绝大多数Bean是Service、Repository、Controller这些“无状态”组件。它们的方法里全是声明局部变量、调用其他组件、读写数据库没有可变的成员状态。这种情况下单例当然安全而且只要维护一份实例内存占用小、启动快、容器管理简单。所以默认单例不是随意的选择而是在“大多数Bean本来就是无状态”的前提下设计出来的。一言以蔽之Spring的单例机制本身没问题问题在于“把一个有状态的类当成Singleton处理”。这个概念不扭过来后面看哪个代码都像有坑。1.2 线程不安全的真正源头可变的实例状态线程安全问题本质上只有一个来源多个线程共享了同一份可变数据并且至少有一个线程在改它。Spring单例Bean处于容器中的唯一实例如果这个Bean里声明了一个成员变量Service public class CounterService { private int count 0; public void increase() { count; } }两个线程同时调increase()count不是原子操作它先读取count、再加1、再写回。两个线程同时读到的可能是同一个值结果就是加了两次但count只多了1。这个就是典型的竞态条件。理解这个问题的关键是分清变量位置方法内部的局部变量是每次调用分配一个自己的空间线程栈私有天然安全成员变量存在堆上被多个线程共享才需要额外保护。所以不要一提到“所有单例都要加锁”那是教条的。无状态单例要不要加锁完全不用。这才是判断线程安全的入口看这个Bean有没有可变状态没有就安全有才需要考虑怎么处理。1.3 无状态Bean为什么不会出事基本前提很多人问“Service单例安全吗Controller单例安全吗”答案是只要这些类里没有可变成员变量它们就是线程安全的。哪怕方法里new了很多对象这些对象都是方法私有的线程之间根本不共享。举个例子Service public class OrderService { public void createOrder(OrderDTO dto) { StringBuilder builder new StringBuilder(); // 用builder拼接日志或者处理订单号 builder.append(dto.getOrderId()); // ... } }每个线程调用createOrder时builder都是自己栈里的局部变量互不干扰。这就是为什么我经常跟团队说写Spring代码的第一个原则是能不用成员变量就不用成员变量让Bean保持无状态。做事之前先把这个前提压进脑子后面一半的坑天然就躲开了。2. Spring单例Bean到底哪些场景会翻车2.1 成员变量是“有状态”的时候最常见的翻车现场就是持有非线程安全的工具类或可变集合。典型代表就是SimpleDateFormat。很多项目里大家图方便在Service里声明一个全局的SimpleDateFormatService public class ReportService { private final SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss); public String formatDate(Date date) { return sdf.format(date); } }SimpleDateFormat的format()方法内部会修改类中维护的Calendar对象是一个典型的有状态非线程安全类。单例Bean里放这样一个全局格式化器并发高的时候就会出现日期错乱、数组越界、NumberFormatException这些奇奇怪怪的异常。而且这类问题飘忽不定时好时坏非常难排查。同理的还有HashMap、ArrayList、StringBuilder等只要是可变集合或非线程安全的工具类在单例成员位置上都是一个隐患。要注意如果给这些容器加了final修饰只是引用不能变内容还是随便改线程安全问题依旧存在。2.2 静态变量和外部缓存带来的坑比成员变量更坑的是静态变量。静态变量属于类本身不只是“单例Bean”层面了连JVM里所有ClassLoader加载的这个类都共享一份。Spring容器管理的是对象生命周期但对静态变量它管不了那么多。最常见的是public class UserCache { public static MapString, UserInfo cache new HashMap(); }如果某些业务往这个静态Map里put数据、其他线程同时读多线程下轻则读到脏数据重则HashMap在扩容时出现死循环JDK7的年代线上CPU直接飙满。还有用静态SimpleDateFormat、静态变量保存“当前登录用户”的属于更隐蔽的坑因为看起来经常能跑通但并发一高就会交叉串数据。拒绝这类写法最彻底的方式不在类里定义静态可变容器如果一定要缓存用Caffeine或Redis这些外部组件或者用ConcurrentHashMap做容器但还是要把业务逻辑上的原子性想清楚。2.3 懒加载并发初始化容易被忽略的隐藏雷区Spring默认在容器启动阶段完成单例Bean的初始化但如果配置了Lazy或者战斗在自定义类中实现了自己的延迟初始化问题就来了。先看一个自己手写的懒加载public class ExpensiveService { private Resource resource; public Resource getResource() { if (resource null) { resource initResource(); // 可能很耗时 } return resource; } }这个代码在单线程下没问题但多线程并发第一次调用getResource()时两个线程都可能看到resource null然后各自初始化最后返回给调用方的可能是不同的Resource实例甚至有的线程拿到一个尚未完全初始化的对象。这个就是典型的双重检查锁使用不当的场景。如果用Lazy延迟初始化Spring Bean还要考虑容器并发初始化多个Bean时的依赖关系。好在Spring的初始化流程大多数时候是串行的真正出问题往往是在并发环境下多个线程同时通过ObjectProvider或ApplicationContext.getBean去触发延迟初始化。要解决优先别把延迟初始化写进业务代码让Spring容器去管理非要自己写必须用正确的双重检查锁加volatile或者干脆用静态内部类Holder的方式避免复杂的并发判断。3. 处理单例线程安全的五大方案3.1 状态外置让Bean无状态一劳永逸这个方案在所有解决方式里排第一位也是最推荐的做法。思路很简单不在单例Bean里保存可变状态所有需要变化的数据通过方法参数传递方法内部用局部变量存放中间结果。接着上面的格式化例子最安全的写法其实是每次在方法里new一个SimpleDateFormatpublic String formatDate(Date date) { SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss); return sdf.format(date); }这个方式看起来“浪费”但SimpleDateFormat本身很轻实例化成本不高业务方法调用频率不算离谱的话这反而最简单可靠。当然如果你对性能有强迫症也可以用ThreadLocal版本后面专门说。状态外置不是不让你用缓存而是让缓存本身独立出去不要作为单例Bean的可变成员变量。比如要缓存统计数据用一个独立的缓存组件内部用自己的并发控制策略业务Service只管调用不直接持有可变Map。这样每个Bean的处理逻辑就成了“输入参数、输出结果”线程之间的隔离是天然成立的。3.2 作用域调节prototype、request、session什么时候用Spring允许把Bean的作用域改成prototype、request、session等。prototype意思是每次获取都新建实例。很多人觉得“那我直接把有状态的Bean改成prototype不就安全了”没那么简单最典型的问题是你把prototype Bean注入到单例Bean里拿到的仍然只有一份。Service public class OrderService { // 注意这里的DateFormater即使标了prototype注入到单例OrderService时也只会有一个实例 Autowired private DateFormater dateFormater; }要让prototype在单例中每次都变成新实例可以用三种方式配置代理模式Scope(value prototype, proxyMode ScopedProxyMode.TARGET_CLASS)依赖ObjectProvider每次调用objectProvider.getObject()拿新实例使用Lookup方法注入最常用的是给类加上proxyModeSpring会创建一个代理对象注入进来每次调用方法时代理对象再从容器获取新的实例。这个方案适合“对象本身有状态且创建成本低”的场景。更符合Web场景的是request作用域和session作用域Component Scope(value request, proxyMode ScopedProxyMode.TARGET_CLASS) public class UserContext { private String userId; // getter/setter }request作用域里每个HTTP请求会拿到独立的UserContext实例天然解决并发问题适合保存一个请求内的上下文数据。但注意如果是异步线程、MQ消费者里取request作用域Bean就会因为找不到绑定在某个请求上的上下文而报错。所以跨线程场景不要依赖request作用域这一点要记牢。3.3 同步机制锁和原子类能用但别依赖如果状态确实要共享而且不能外置、不能换作用域那就需要用同步机制。最传统的做法是synchronized或Lock但同步会阻塞线程在高并发下容易成为性能瓶颈。而且很多人容易加大锁的范围把一个完整方法用synchronized包住导致明明可以不互斥的逻辑也排队执行。我的经验是优先用原子类和并发容器避免锁的沉重感。比如上面计数器的例子private AtomicInteger count new AtomicInteger(0); public void increase() { count.incrementAndGet(); }AtomicInteger内部利用CAS实现线程安全的递增在低冲突场景下表现很好。如果状态是个更复杂的结构可以用ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue这些并发容器它们本身保证了内部操作的基本线程安全大部分场景比手动加锁靠谱。真的需要锁的时候记得锁的范围越小越好。比如你要读一个Map的旧值计算后写入新值这个“读-改-写”过程必须整体原子这时候用ConcurrentHashMap.computeIfAbsent之类的方法反而比自己加锁更合适。所以在犹豫要不要上synchronized之前先翻一翻JDK并发包大概率有现成的工具帮你把并发细节消化掉。3.4 线程私有化ThreadLocal的正确打开方式ThreadLocal的原理很简单每个线程都有一个自己的副本存储区多线程访问同一个ThreadLocal对象时实际上各拿各的副本互不干扰。它非常适合用来保存“线程私有的、可复用的对象”典型场景就是SimpleDateFormat。改造后的安全写法private static final ThreadLocalSimpleDateFormat DATE_FORMAT ThreadLocal.withInitial(() - new SimpleDateFormat(yyyy-MM-dd HH:mm:ss)); public String formatDate(Date date) { return DATE_FORMAT.get().format(date); }这段代码既避免了每次new的开销又让每个线程都有自己的SimpleDateFormat互不干扰。注意几个使用细节ThreadLocal可不是“免费”的它是有内存泄漏风险的。在线程池场景里线程执行完任务不会被销毁ThreadLocal里的值还挂在Thread对象上。如果这个值很大或者持有的是带业务上下文的强引用就会一直占内存。用完了一定要remove特别是保存了本次请求的用户信息、traceId这些上下文时不要指望线程下个任务会覆盖。子线程默认拿不到父线程的ThreadLocal需要InheritableThreadLocal或者TransmittableThreadLocal才能跨线程传递而且这里的传递机制有很多边界情况不是随手就能用的。所以我的建议是ThreadLocal适合保存“线程内的临时上下文”不适合保存大对象、长生命周期对象如果只是某个工具类非线程安全换成局部new可能更简单。3.5 不可变对象与并发容器兜底最后再说两个偏防御的方案。一是设计上尽量不可变。所谓不可变对象就是对象创建后状态就不能被修改了重要字段都用final修饰不提供setter。比如缓存配置信息、字典映射、环境参数它们本来就不需要修改你把它做成不可变类在多线程环境下天然安全。二是并发容器前面已经提过这里强调一下它的适用范围。ConcurrentHashMap只能保证单独的put、get、compute等操作是原子的但如果你需要先判断存在再更新这样一个复合操作仍然需要额外的同步处理。很多同学以为用了ConcurrentHashMap就万事大吉结果在“先查后写”的业务逻辑里还是出了并发重复执行的问题。正确的做法是用它提供的computeIfAbsent、merge这类原子复合方法让整个操作在容器内部完成业务代码不留中间间隙。这两招单独用起来可能不够但配合状态外置和无状态原则能把整个Bean的线程安全性提升一大截。4. 一次真实案例现象、排查、修复全流程4.1 现象描述日期偶发错乱报警信息看不懂这个案例是我在某一个模拟项目中真实排查过的用来说明整个过程再合适不过。项目是一个报表导出服务用户会按天导出大量报表。某天开始监控时不时报警内容是导出文件名里的日期不对。日志里有的文件名变成了2024-12-31但业务时间其实是2025-06-01更离谱的是还会出现月份为13、日期为00的情况。一开始大家以为是日期计算逻辑写错了查了业务入参也没发现问题。后来有一个同事发现同样一段代码在本地单线程跑完全正常但线上并发一高就开始乱。于是大家开始怀疑多线程问题。4.2 排查过程从日志到字节码再到对象引用排查第一步是加日志。在报错的文件名生成处把线程ID、某个关键时间戳、格式化结果都打印出来。很快发现同一个时间戳入参在不同线程执行格式化得到的结果不一样。这就明确指向了“共享的格式化器被并发修改”。第二步是看代码。找到生成文件名的地方果然是在一个Service单例Bean里声明了private final SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd);再往深查一层这个sdf被用在了好几个方法里有的方法甚至还会调用sdf.applyPattern()改成另一种格式。这就更严重了不只并发format有问题连格式都可能被别的线程改掉。这个细节给排查提供了铁证工具类状态在被并发篡改。第三步是验证。既然怀疑是SimpleDateFormat并发导致就没必要在生产环境贸然改代码在压测环境用100个线程同时调用这个方法很快复现出月份13、日期0的错乱。实际上SimpleDateFormat内部把解析和格式化的中间状态保存在一个Calendar对象里多个线程共用同一份Calendar数据串来串去就成了这个结果。4.3 修复方案与线上验证修复方式我选了ThreadLocal因为导出服务对DateFormater的创建频率比较敏感不想每个文件名都在方法里new一次。代码改成了private static final ThreadLocalSimpleDateFormat SDF_HOLDER ThreadLocal.withInitial(() - new SimpleDateFormat(yyyy-MM-dd));使用SDF_HOLDER.get()替换原来的sdf并且导出任务结束后在finally里SDF_HOLDER.remove()。这个改动前后不到10行线上的日期错乱问题立刻消失。这个案例给我最大的教训是并发问题不一定表现为程序崩溃更多时候是“数据悄悄变脏”。排查路径其实不复杂但难在愿不愿意怀疑一个大家都写着玩的SimpleDateFormat。如果你在生产环境也遇到诡异的数据串来串去先看一眼这个类是不是被多个线程共用了很多时候这个直觉能帮你省半天时间。5. 实战速查表与避坑清单5.1 症状、原因、解决方案对照表我在下面整理了一个速查表按“现象 - 可能原因 - 解决方案”来归类。不一定覆盖所有场景但基本能帮你从现象反推。现象可能原因解决方案日期错乱、月份/日期越界SimpleDateFormat被多个线程共享ThreadLocal副本或方法内new计数值不准、扣减超量单例Bean内int/long自增非原子AtomicLong/AtomicInteger或加锁缓存偶尔读不到最新值、脏数据直接使用HashMap做共享缓存ConcurrentHashMap compute系列方法频繁“登录用户串号”静态变量保存用户上下文使用请求作用域Bean或传递参数懒加载对象状态不稳定自己写的if null初始化并发交给Spring管理或用DCLvolatile列表偶尔出现重复/缺失ArrayList在多线程读写CopyOnWriteArrayList或加锁这张表的核心还是那句话先判断共享的可变状态在哪再选择对应的隔离或同步手段。不要一上来就想到锁锁是最后手段。5.2 面试里我被追问最多的三个问题面试官问“Spring单例Bean线程安全吗”标准答案是Spring容器本身不负责Bean的线程安全默认单例只是创建模式线程安全要看Bean自身是否包含可变共享状态。问得更深的三连击也很常见第一问既然Spring会并发创建Bean那初始化过程安全吗Spring容器初始化单例Bean大多在启动阶段单线程完成启动后所有线程只是读实例引用不会触发重复创建。只要你不手写懒加载这层一般不用操心。第二问prototype能完全替代ThreadLocal吗不能。prototype解决的是“每次新建实例”但如果你把它注入进单例Bean这个新实例还是被共享的。而且频繁创建对象会带来GC开销如果只是需要一个简单副本ThreadLocal更轻。第三问并发编程时为什么我改了volatile还是出问题volatile只保证可见性和禁止指令重排不保证复合操作的原子性。典型如count这种“读-改-写”操作volatile顶不住。用AtomicInteger或锁才能解决复合操作问题。这三个问题是同一个知识体系你能不能区分“对象生命周期”和“并发访问模式”。Spring管的是前者线程安全是后者。5.3 写Spring代码时我给自己定的规矩常年踩坑之后我自己总结了一套编写Spring代码的小规矩在这里分享出来。第一Controller和Service尽量不声明可变的成员变量所有会变化的数据都以参数方式传入传出。第二如果确实需要持有工具类先查一下它是不是线程安全的不确定就去文档或源码里看。第三做一个并发自检把自己写的Bean想象成被100个线程同时调用变量会不会打架第四能用JUC并发容器解决就不要用synchronized能用ThreadLocal就不要加锁能设计成无状态就最好。最后再分享一个查问题的技巧遇到线上偶发错误先看日志里的线程ID是不是同一个然后看报错字段是不是来自单例Bean里的某个共享对象最后看这个对象内部有没有会变化的中间状态。按这个流程走80%的Spring线程安全坑都能快速定位。如果你能把“单例是容器的一种管理方式线程安全是并发访问的一种客观属性”这两件事分开理解这个知识点基本就吃透了后面写代码也会少踩很多雷。

相关新闻

Java集合框架核心:从Collection接口到List与Set实现原理与选型

Java集合框架核心:从Collection接口到List与Set实现原理与选型

1. 集合框架的起点:为什么必须先搞懂Collection接口很多同学刚开始学Java集合时,容易一头扎进ArrayList、HashMap的源码里,背了一堆方法名和底层结构,真到写代码的时候却不知道该选哪个。这个问题我见过太多次了,归根结…

2026/10/13 0:21:51 阅读更多 →
火山引擎服务器实战:安全组、SSH排错与监控告警全攻略

火山引擎服务器实战:安全组、SSH排错与监控告警全攻略

我最近把一个跑了大半年的个人项目迁到了一台火山引擎服务器上,折腾了整整一个周末才把所有事理顺。过程中踩的坑、绕的路比预想中多不少,但也正因为这一趟,才把云服务器从开通到稳定运行全链路摸了个透。这篇不打算做成那种纯复制官方文档的…

2026/10/13 0:21:51 阅读更多 →
PostgreSQL覆盖索引实战:用INCLUDE消除回表,让慢查询从15ms降到1.8ms

PostgreSQL覆盖索引实战:用INCLUDE消除回表,让慢查询从15ms降到1.8ms

半月前我被人拉去看一条订单查询的慢SQL,单看执行计划其实不算难看:索引确实命中了,过滤条件也走了,可整体就是磨蹭了大几百毫秒。翻完字段才知道,真正吃掉时间的不是锁、不是排序,而是每条满足条件的记录都…

2026/10/13 0:21:51 阅读更多 →

最新新闻

CentOS 7.x下用Docker与Shell脚本一键部署Redis Cluster集群

CentOS 7.x下用Docker与Shell脚本一键部署Redis Cluster集群

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:09:51 阅读更多 →
AI芯片软硬件协同优化实战:寄存器可配置化设计方法

AI芯片软硬件协同优化实战:寄存器可配置化设计方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:09:51 阅读更多 →
C#虚属性与抽象属性重写详解:以Label控件为例

C#虚属性与抽象属性重写详解:以Label控件为例

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:09:51 阅读更多 →
NFine框架登录流程源码深度解析:从MVC分层到权限体系实战

NFine框架登录流程源码深度解析:从MVC分层到权限体系实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:09:51 阅读更多 →
STM32 Systick滴答定时器:从原理到实战的精准延时方案

STM32 Systick滴答定时器:从原理到实战的精准延时方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:09:51 阅读更多 →
基于开路电压的改进型MPPT算法:快速辐照变化下光伏逆变器的可靠跟踪方案

基于开路电压的改进型MPPT算法:快速辐照变化下光伏逆变器的可靠跟踪方案

快速变天时,光伏逆变器最怕的不是功率不够,而是明明太阳明晃晃地照着,MPPT却在“原地打转”——电压来回扫,功率丢掉一大截。我这些年调试组串式逆变器,遇到最多的就是这种快速变化辐照下的MPPT失效问题,尤…

2026/10/13 1:08:51 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →