C# AOP 实战:从装饰器到 Castle 动态代理的选型与落地
1. 从一次代码维护的噩梦说起你有没有遇到过这样的场景一个业务方法写着写着日志、缓存、权限校验、事务控制、异常处理全塞在一起核心业务逻辑被淹没在几十行“周边代码”里。改一个校验规则要在十几个方法里重复改加一行日志得挨个文件翻。这种代码我维护过不止一次每次都想掀桌子。后来我接触到一个思路叫AOPAspect-Oriented Programming面向切面编程。它不是什么新语言也不是框架专属的黑魔法而是一种编程范式——把那些横跨多个模块的“周边逻辑”抽出来集中管理让业务代码回归纯粹。C# 里实现 AOP 的路子有好几条从最轻量的装饰器模式到DispatchProxy动态代理再到Castle.DynamicProxy这类成熟库甚至编译期织入的PostSharp商业方案。不同方案在性能、侵入性、调试友好度上差异很大选错了后期维护成本极高。这篇内容适合已经写过一段时间 C#、对委托、反射、特性Attribute有一定了解但还没系统用过 AOP 的开发者。我会从设计思路讲到具体落地把每种方案的取舍逻辑、参数配置、踩坑经验都摊开说。看完你至少能做到两件事知道什么场景该上 AOP、什么场景不该上能照着文中的代码把一套可用的切面框架搭起来。2. 为什么要在 C# 里引入 AOP2.1 横切关注点到底带来了什么麻烦先明确一个概念横切关注点Cross-Cutting Concern。日志记录、性能监控、缓存、事务、权限验证、重试、幂等控制这些东西的特点是——它们不属于某一个业务模块但几乎每个业务模块都需要。如果按传统 OOP 的写法它们会像藤蔓一样缠在每个方法里。我拿一个订单创建方法举例。纯业务逻辑可能只有三行校验库存、生成订单、扣减库存。但实际代码里往往长这样方法入口打日志、检查用户权限、开启事务、检查缓存、执行核心逻辑、提交事务、写操作日志、捕获异常并回滚、记录耗时。核心三行被十几行周边代码包夹读代码的人得先拨开这些“杂草”才能看到业务本身。这种写法的直接后果有三个。第一重复。同样的日志模板在几十个方法里复制粘贴改一次格式要动几十处。第二耦合。业务逻辑和基础设施逻辑绑死想单独测试业务逻辑得先把日志、事务这些依赖 mock 掉。第三遗漏。靠人工保证每个方法都记得加事务、加日志迟早会漏而且漏了很难发现。2.2 AOP 的核心思路把“周边”抽出来AOP 的解法很直接把这些横切逻辑从业务方法里抽离定义成独立的切面Aspect然后通过某种机制“织入Weave”到目标方法的前后。业务代码里只留核心逻辑日志、事务这些由切面统一处理。这里有几个术语需要先对齐不然后面看代码会懵切面Aspect横切逻辑的封装单元比如一个“日志切面”或“事务切面”。连接点Join Point程序执行过程中可以插入切面的点在 C# 里通常就是方法调用。切入点Pointcut一组连接点的筛选规则比如“所有名字以 Service 结尾的类里的 public 方法”。通知Advice切面在连接点上执行的具体动作分前置Before、后置After、环绕Around、异常AfterThrowing等类型。织入Weaving把切面应用到目标方法的过程可以在编译期、加载期或运行期完成。理解这几个词之后你会发现 AOP 本质上是“在方法调用的管道上插一层拦截器”。至于这层拦截器怎么插进去就是不同实现方案的分水岭。2.3 什么场景该用什么场景别碰AOP 不是银弹滥用比不用更糟。我的经验判断标准是这样的适合用的场景日志和审计尤其是需要统一格式的、性能计时、缓存读写、事务边界管理、权限校验、统一异常包装、重试策略。这些逻辑的共同点是——与业务无关、需要跨多个方法复用、对调用方透明。不适合用的场景核心业务规则这本来就该写在业务方法里、只有一两个方法需要的逻辑直接写更清晰、对性能极度敏感的路径动态代理有开销、调试困难的复杂切面嵌套出问题时堆栈会很难看。一个实用的判断口诀如果这段逻辑你会在三个以上的方法里重复写且它不改变业务语义那它大概率适合做成切面。反之如果它本身就是业务的一部分别硬抽。3. C# 实现 AOP 的几条主流路线3.1 装饰器模式最朴素也最可控装饰器模式Decorator Pattern是最原始的“手动 AOP”。思路是定义一个和目标类实现同一接口的装饰类在装饰类里调用目标方法前后插入逻辑。public interface IOrderService { void CreateOrder(Order order); } public class OrderService : IOrderService { public void CreateOrder(Order order) { // 纯业务逻辑 } } public class LoggingOrderServiceDecorator : IOrderService { private readonly IOrderService _inner; private readonly ILogger _logger; public LoggingOrderServiceDecorator(IOrderService inner, ILogger logger) { _inner inner; _logger logger; } public void CreateOrder(Order order) { _logger.LogInformation(开始创建订单 {OrderId}, order.Id); var sw Stopwatch.StartNew(); try { _inner.CreateOrder(order); _logger.LogInformation(订单创建成功耗时 {Elapsed}ms, sw.ElapsedMilliseconds); } catch (Exception ex) { _logger.LogError(ex, 订单创建失败); throw; } } }这种方式的优点是零魔法、完全可控、调试友好堆栈清晰性能开销几乎为零。缺点是每个要增强的接口都得手写一个装饰类接口一多就是灾难。它适合接口数量少、增强逻辑固定的场景比如只有三五个核心服务需要加日志。3.2 DispatchProxy.NET 自带的动态代理从 .NET Core 开始System.Reflection.DispatchProxy提供了运行期动态生成代理的能力不需要引入第三方库。它的原理是你定义一个继承自DispatchProxy的类重写Invoke方法所有对目标接口的调用都会先进入Invoke你可以在里面决定怎么处理。public class LoggingProxyT : DispatchProxy { private T _target; private ILogger _logger; protected override object Invoke(MethodInfo targetMethod, object[] args) { _logger.LogInformation(调用 {Method}, targetMethod.Name); var sw Stopwatch.StartNew(); try { var result targetMethod.Invoke(_target, args); _logger.LogInformation({Method} 完成耗时 {Elapsed}ms, targetMethod.Name, sw.ElapsedMilliseconds); return result; } catch (TargetInvocationException ex) { _logger.LogError(ex.InnerException, {Method} 异常, targetMethod.Name); throw ex.InnerException; } } public static T Create(T target, ILogger logger) { var proxy CreateT, LoggingProxyT() as LoggingProxyT; proxy._target target; proxy._logger logger; return (T)(object)proxy; } }用的时候这样调var realService new OrderService(); var proxy LoggingProxyIOrderService.Create(realService, logger); proxy.CreateOrder(order);DispatchProxy的好处是框架自带、无需第三方依赖适合不想引入额外包的项目。但它有几个硬伤只能代理接口不能代理类、每次调用都走反射、性能比直接调用慢一个数量级、对异步方法的支持需要额外处理返回Task时要小心Invoke的同步语义。我在高并发场景实测过纯反射代理的吞吐量大概只有直接调用的三分之一到一半所以它更适合中低频调用的服务层。3.3 Castle.DynamicProxy生产环境的主力方案如果要在生产环境大规模用 AOPCastle.DynamicProxy基本是 C# 生态里的默认选择。它通过运行期生成 IL 代码来创建代理类性能比纯反射好很多而且支持代理类和接口、支持异步方法、支持拦截器链。核心是定义一个拦截器public class LoggingInterceptor : IInterceptor { private readonly ILogger _logger; public LoggingInterceptor(ILogger logger) _logger logger; public void Intercept(IInvocation invocation) { _logger.LogInformation(进入 {Method}, invocation.Method.Name); var sw Stopwatch.StartNew(); try { invocation.Proceed(); // 执行目标方法 _logger.LogInformation({Method} 完成耗时 {Elapsed}ms, invocation.Method.Name, sw.ElapsedMilliseconds); } catch (Exception ex) { _logger.LogError(ex, {Method} 异常, invocation.Method.Name); throw; } } }创建代理并调用var generator new ProxyGenerator(); var interceptor new LoggingInterceptor(logger); var proxy generator.CreateClassProxyOrderService(interceptor); proxy.CreateOrder(order);Castle.DynamicProxy的关键优势在于支持类代理不强制接口、拦截器可组合多个切面按顺序执行、性能可接受IL 生成后接近原生调用。代价是引入了第三方依赖且代理类不能是 sealed、被拦截的方法必须是 virtual。这一点经常被忽略——如果你忘了给方法加virtual拦截器会静默失效不报错只是不生效排查起来很费时间。3.4 编译期织入性能最好但门槛最高还有一类方案是在编译期就把切面代码织入目标程序集代表是PostSharp商业和Fody开源。它们的优点是运行时零开销因为切面代码已经变成普通 IL 了缺点是构建流程变复杂、调试时看到的代码和源码不一致、PostSharp还要收费。我的建议是除非你有极致的性能要求或者团队已经有一套成熟的编译期织入流程否则优先选Castle.DynamicProxy。它在性能、灵活性、生态成熟度之间取得了最好的平衡。3.5 方案选型对照表方案侵入性性能支持类代理依赖适用场景装饰器模式低手写最高是无接口少、逻辑固定DispatchProxy低中低否仅接口无轻量、无第三方依赖Castle.DynamicProxy低高是第三方包生产环境主力编译期织入中最高是工具链极致性能要求4. 用 Castle.DynamicProxy 搭一套可用的切面框架4.1 拦截器的执行顺序与组合实际项目里往往不止一个切面日志、缓存、事务、重试可能同时存在。Castle.DynamicProxy支持传入多个拦截器它们按数组顺序形成一条链每个拦截器调用invocation.Proceed()时会把控制权交给下一个。顺序很关键。我的经验排序是日志 → 性能计时 → 缓存 → 事务 → 重试 → 业务。日志放最外层保证任何情况都能记录到调用缓存放在事务之前命中缓存就不必开事务重试放在最内层靠近业务避免把事务重复开启。var interceptors new IInterceptor[] { new LoggingInterceptor(logger), new TimingInterceptor(metrics), new CacheInterceptor(cache), new TransactionInterceptor(unitOfWork), new RetryInterceptor(maxRetries: 3) }; var proxy generator.CreateClassProxyOrderService(interceptors);注意拦截器链里任何一个不调用Proceed()后面的拦截器和目标方法都不会执行。缓存拦截器命中时就应该直接返回结果、不调Proceed()这是正常行为但要确保返回值类型正确。4.2 基于特性的切入点筛选如果所有方法都走同一套拦截器粒度太粗。更精细的做法是用特性Attribute标记需要增强的方法拦截器里判断特性是否存在。[AttributeUsage(AttributeTargets.Method)] public class CacheableAttribute : Attribute { public int Seconds { get; set; } 60; } public class CacheInterceptor : IInterceptor { private readonly IMemoryCache _cache; public void Intercept(IInvocation invocation) { var attr invocation.Method.GetCustomAttributeCacheableAttribute(); if (attr null) { invocation.Proceed(); return; } var key BuildCacheKey(invocation); if (_cache.TryGetValue(key, out var cached)) { invocation.ReturnValue cached; return; } invocation.Proceed(); _cache.Set(key, invocation.ReturnValue, TimeSpan.FromSeconds(attr.Seconds)); } private string BuildCacheKey(IInvocation invocation) { var args string.Join(,, invocation.Arguments.Select(a a?.ToString())); return ${invocation.Method.DeclaringType.Name}.{invocation.Method.Name}:{args}; } }这样只有标了[Cacheable]的方法才会走缓存逻辑其他方法直接放行。切入点规则完全由你自己控制比全局拦截灵活得多。4.3 异步方法的拦截陷阱C# 里异步方法返回Task或TaskT拦截器如果按同步方式处理会出问题。invocation.Proceed()对异步方法来说返回的是Task对象本身而不是await之后的结果。如果你在Proceed()之后直接读invocation.ReturnValue拿到的是未完成的Task。正确的做法是判断返回类型对Task做awaitpublic void Intercept(IInvocation invocation) { var returnType invocation.Method.ReturnType; if (typeof(Task).IsAssignableFrom(returnType)) { invocation.Proceed(); var task (Task)invocation.ReturnValue; invocation.ReturnValue InterceptAsync(task, invocation, returnType); } else { invocation.Proceed(); } } private async Task InterceptAsync(Task task, IInvocation invocation, Type returnType) { var sw Stopwatch.StartNew(); try { await task.ConfigureAwait(false); _logger.LogInformation({Method} 异步完成耗时 {Elapsed}ms, invocation.Method.Name, sw.ElapsedMilliseconds); } catch (Exception ex) { _logger.LogError(ex, {Method} 异步异常, invocation.Method.Name); throw; } }如果返回的是TaskT还需要把结果正确包装回去否则调用方拿到的类型不对。这块是异步 AOP 最容易翻车的地方我见过不少项目在这里踩坑表现为“日志记了但结果丢了”或者“异常被吞了”。4.4 与依赖注入容器集成手动new ProxyGenerator()创建代理在小型项目里够用但正规项目一般用 DI 容器。以Microsoft.Extensions.DependencyInjection为例可以写一个扩展方法在注册服务时自动套上代理。public static IServiceCollection AddProxiedServiceTInterface, TImpl( this IServiceCollection services, params IInterceptor[] interceptors) where TInterface : class where TImpl : class, TInterface { services.AddSingletonTImpl(); services.AddSingletonTInterface(sp { var target sp.GetRequiredServiceTImpl(); var generator new ProxyGenerator(); return generator.CreateInterfaceProxyWithTargetTInterface(target, interceptors); }); return services; }调用时services.AddProxiedServiceIOrderService, OrderService( new LoggingInterceptor(logger), new CacheInterceptor(cache));这样业务代码里注入的IOrderService就是代理对象切面自动生效业务类本身完全不知道 AOP 的存在。这种“对调用方透明”正是 AOP 想要的效果。5. 实操中踩过的坑与排查技巧5.1 拦截器不生效的几种典型原因这是最高频的问题我整理成速查表现象可能原因排查方法拦截器完全不执行方法不是 virtual检查方法签名加 virtual拦截器完全不执行类被 sealed去掉 sealed部分方法不执行调用了非代理对象确认注入的是代理而非原始实例异步方法结果丢失未正确处理 Task检查 ReturnValue 包装逻辑异常被吞拦截器未 rethrow检查 catch 块是否 throw缓存永远不命中缓存 key 含随机值打印 key 确认参数稳定性其中“方法不是 virtual”是最隐蔽的。Castle.DynamicProxy创建类代理时只能重写 virtual 方法非 virtual 方法它无能为力而且不会报错。我建议在团队规范里明确所有需要被拦截的 public 方法一律加 virtual或者干脆统一用接口代理从根上避免这个问题。5.2 性能开销的实测与优化动态代理不是没有成本的。我在一台普通开发机上做过粗略测试直接调用一个空方法大约 2 纳秒Castle.DynamicProxy代理后大约 50 到 100 纳秒DispatchProxy反射代理大约 300 到 500 纳秒。差距看起来大但放到真实业务里一次数据库查询动辄几毫秒这点代理开销完全可以忽略。真正需要警惕的是拦截器内部的逻辑。比如在拦截器里做字符串拼接、反射遍历参数、序列化对象这些才是性能杀手。我的优化经验是日志拦截器里用结构化日志的占位符而不是字符串拼接缓存 key 的生成避免深度反射性能计时用Stopwatch而不是DateTime.Now。一个反直觉的经验拦截器链越长性能下降不是线性的。因为每个拦截器都要走一次虚方法调用和委托链长到五六个以上时开销会明显起来。所以别把切面拆得太碎能合并的合并。5.3 调试与日志的实用技巧AOP 让代码“隐形”了出问题时堆栈里全是代理类的名字很难定位。我的做法是在拦截器里记录足够上下文目标类型、方法名、参数摘要、耗时、异常。但参数摘要要小心别把敏感信息密码、令牌打进日志。另一个技巧是给拦截器加一个开关通过配置控制是否启用。开发环境全开方便调试生产环境按需开启避免日志量爆炸。可以用IOptions注入一个配置对象在Intercept开头判断。if (!_options.Enabled) { invocation.Proceed(); return; }还有一点拦截器本身要尽量无状态、线程安全。因为代理对象通常是单例拦截器会被并发调用任何共享的可变状态都要加锁或者用并发容器。6. 几个真实场景的落地思路6.1 统一异常包装业务层抛出的异常往往五花八门上层需要统一的错误码和消息。用异常拦截器可以把各种底层异常翻译成统一的业务异常同时记录原始异常用于排查。关键点是保留InnerException别把原始堆栈丢了。6.2 幂等控制对于创建订单、支付这类操作重复提交是常见问题。可以做一个幂等拦截器根据方法上的[Idempotent]特性和请求参数生成唯一键在 Redis 里做占位重复请求直接返回上次结果。这个切面把幂等逻辑从每个业务方法里抽出来非常干净。6.3 分布式追踪埋点在微服务架构里每个服务调用都需要传递追踪 ID。用拦截器在方法入口读取或生成 TraceId写入日志上下文方法结束时清理。这样所有日志自动带上链路信息排查跨服务问题效率提升明显。这几个场景的共同点是逻辑统一、与业务无关、需要跨方法复用——正好是 AOP 的甜区。反过来如果某个逻辑只在一个方法里用或者它本身就是业务规则的一部分老老实实写在方法里别为了用 AOP 而用 AOP。我在实际项目里推行 AOP 时最大的阻力不是技术而是团队习惯。有人觉得“看不见的代码”不踏实有人担心性能。我的应对方式是先从日志和计时这两个最无争议的切面切入让大家看到收益等团队接受了再逐步引入缓存、事务这些更复杂的切面。技术方案能不能落地往往取决于推进节奏而不是方案本身有多先进。

相关新闻

Activiti与Flowable选型对比:从API差异到业务场景适配

Activiti与Flowable选型对比:从API差异到业务场景适配

每隔一阵子就会有人问我:公司要做审批流,Activiti和Flowable到底选哪个?最近我刚帮一个客户做完流程引擎的迁移评估,把这个问题从头到尾又捋了一遍。这两个引擎同根同源,但在API设计、模块拆分、社区维护、授权模式上的…

2026/10/12 8:40:22 阅读更多 →
设计模式基础篇第28讲:备忘录模式——用TaoToken统一Key保存对象状态,实现撤销与回滚

设计模式基础篇第28讲:备忘录模式——用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 8:40:22 阅读更多 →
SpringBoot+小程序智慧医院系统:从挂号到AI问诊落地

SpringBoot+小程序智慧医院系统:从挂号到AI问诊落地

在医院信息化这个赛道里摸爬滚打了几年,我一直觉得最难的其实不是技术,而是把业务流程真正跑通。去年接手了一个智慧医院管理系统的项目,技术栈定在 SpringBoot 微信小程序,还要带一个 AI 智能问诊模块。今天把整个项目的落地过程…

2026/10/12 8:39:21 阅读更多 →

最新新闻

稀疏视角新视角合成:VGGT-Diff几何引导视频扩散模型实战

稀疏视角新视角合成:VGGT-Diff几何引导视频扩散模型实战

1. 稀疏视角新视角合成到底难在哪1.1 从“拍几张照片就能环视物体”说起新视角合成(Novel View Synthesis,NVS)这个任务,说人话就是:给你若干张从不同角度拍的照片,让你“脑补”出从其他没拍过的角度看上去…

2026/10/12 9:24:59 阅读更多 →
给对话AI装上“外置大脑”:Claude外部记忆工具实战指南

给对话AI装上“外置大脑”:Claude外部记忆工具实战指南

1. 为什么需要给Claude装上“记忆”:从一个经常断线的项目说起做AI开发的人大概都经历过这种崩溃瞬间:刚跟对话式助手聊到关键部分,聊了几十轮以后它突然把一个小时前你自己确认过的技术选型给忘了,让你重新解释一遍需求&#xff…

2026/10/12 9:24:59 阅读更多 →
Hyperf 安装部署完全指南:环境要求、Composer 脚手架与 Docker 开发实践

Hyperf 安装部署完全指南:环境要求、Composer 脚手架与 Docker 开发实践

后端Web框架微服务RPC框架异步编程 【免费下载链接】hyperf 🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease. 项目地址: https://gitcode.com/hyperf/hyperf 点击查看 免费下载 …

2026/10/12 9:24:59 阅读更多 →
SAML 2.0 SSO单点登录实战:从XML断言解析到会话治理避坑指南

SAML 2.0 SSO单点登录实战:从XML断言解析到会话治理避坑指南

简介:一份面向.NET与Java开发者的SAML 2.0单点登录实现资源包,覆盖VS2005、VS2008、VS2010三个版本,并附带部分Java代码与说明文档。包内完整展示了客户端发起SAML请求、SSO响应验证信任、加密断言返回、客户端解密及有效性校验的登录闭环&am…

2026/10/12 9:24:59 阅读更多 →
轻型AI中台:面向中企的OCR+规则引擎数据协同方案

轻型AI中台:面向中企的OCR+规则引擎数据协同方案

1. 项目概述:为什么一个“轻型AI中台”能真正撬动业务一线的痛点我做企业数字化落地快十二年了,从最早帮工厂装ERP,到后来给银行做RPA流程自动化,再到最近三年密集接触中小金融机构、区域型连锁零售、本地生活服务商——发现一个反…

2026/10/12 9:24:59 阅读更多 →
数据复制深度拆解:从HDFS块复制到CDC同步的机制、实践与容灾设计

数据复制深度拆解:从HDFS块复制到CDC同步的机制、实践与容灾设计

大数据领域做久了,你会发现一个很朴素但又绕不开的现实: 数据复制 几乎贯穿了所有核心系统的生命周期。无论是离线数仓里的表同步、实时链路里的CDC采集,还是跨机房容灾里的镜像复制,本质上都在做同一件事——把一份数据从A点搬…

2026/10/12 9:23:59 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战: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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →