SpringBoot AOP统一Web请求日志:从原理到生产级实现
1. 项目缘起为什么我们需要统一处理Web请求日志在任何一个后端服务里日志都是我们排查问题的“眼睛”。尤其是Web请求日志它记录了谁、在什么时候、用什么方式、访问了哪个接口、得到了什么结果。当线上出现一个诡异的接口超时或者用户反馈“我的操作没生效”时第一反应是什么对就是去翻日志。但如果你还在用最原始的方式在每个Controller方法里手动写log.info(“收到请求参数是{}”, param)那很快就会陷入几个困境。首先代码严重重复每个方法开头结尾都是那几行日志代码枯燥且容易出错。其次日志格式不统一张三喜欢打印JSON李四喜欢用逗号分隔排查时看得眼花缭乱。最关键的是你很容易遗漏关键信息比如处理耗时、用户IP、请求IDTraceId等而这些信息在分布式链路追踪时至关重要。所以统一处理Web请求日志本质上是在做两件事一是通过技术手段消灭重复劳动提升开发效率二是标准化日志输出为后续的监控、告警、链路追踪打下坚实基础。SpringBoot AOP的组合正是解决这个问题的“银弹”。AOP面向切面编程允许我们将这些横跨多个模块的公共行为日志记录从业务逻辑中剥离出来集中到一个地方进行声明和管理。这不仅仅是写几行代码更是一种工程化的思维。2. AOP核心概念扫盲切面、连接点与通知在动手之前我们得先统一语言。AOP里有些概念听起来有点玄乎但其实用生活中的例子一比喻就懂了。假设你的项目是一栋大楼每个Controller方法就是一个房间。连接点 (Join Point) 这栋楼里所有可能被“增强”的地方比如每个房间的门口方法执行前、窗户方法异常时、后门方法执行后。在Spring AOP中连接点特指方法的执行。切点 (Pointcut) 你不是要对所有房间都做同样的事。你可能只想监控“所有总经理办公室”或者“所有带窗户的会议室”。切点就是一个表达式用来匹配和筛选你感兴趣的连接点。比如execution(* com.example.controller..*.*(..))这个表达式就匹配了com.example.controller包及其子包下所有类的所有方法。通知 (Advice) 你想在那些被选中的地方切点具体做什么。这就是通知。它定义了增强行为的时机和内容。Before: 在房间门口放个迎宾员方法执行前。AfterReturning: 客人满意离开后在门口做个登记方法成功返回后。AfterThrowing: 客人生气摔门而出时记录下投诉原因方法抛出异常后。After: 不管客人是满意还是生气只要他离开了房间就打扫一下卫生方法执行后无论成功或异常。Around: 这是最强大的保安他可以在客人进门前检查证件前置处理在客人进入后全程陪同执行原方法在客人离开时出具访客记录后置处理甚至有权不让客人进入阻止方法执行。切面 (Aspect) 把上面这些概念组合起来形成一个完整的方案。比如“大楼安全监控方案”这个切面就包含了监控哪些房间切点、在什么时机通知类型、做什么具体动作通知内容。在我们的项目里“统一请求日志切面”就是一个具体的Aspect。理解了这些你就知道我们要做的就是定义一个切面写一个切点来匹配所有Web请求方法然后用Around通知把这些方法的进、出、耗时、参数、结果都记录下来。3. 实战构建从零搭建一个生产可用的日志切面光说不练假把式我们直接上代码。我会用一个围绕Controller层设计的Around通知作为核心因为它能提供最全面的控制。3.1 基础依赖与环境准备首先确保你的pom.xml里已经引入了SpringBoot的Web和AOP依赖。一般来说用Spring Initializr创建项目时勾选Spring Web和Spring AOP即可。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency如果你需要更丰富的日志内容比如获取HTTP请求对象spring-boot-starter-web已经包含了必要的Servlet API。另外推荐使用SLF4J Logback/Log4j2作为日志框架SpringBoot默认已经集成好了。3.2 核心切面类设计与实现我们来创建一个名为WebLogAspect的切面类。这里有几个关键设计点使用Around因为它能完全控制方法的执行方便计算耗时和在异常情况下也能记录日志。定义清晰的切点精准匹配Web控制器层的方法避免切入到Service或Dao层造成日志泛滥。获取完整的上下文信息包括请求IP、URL、参数、方法、用户标识等。使用MDC实现请求链路追踪为同一个请求的所有日志打上同一个traceId这在微服务环境下是黄金标准。package com.example.demo.aspect; import com.fasterxml.jackson.databind.ObjectMapper; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.http.HttpServletRequest; import java.util.UUID; /** * 统一Web请求日志处理切面 */ Aspect Component public class WebLogAspect { private static final Logger LOGGER LoggerFactory.getLogger(WebLogAspect.class); private final ObjectMapper objectMapper new ObjectMapper(); /** * 定义切点匹配controller包下的所有公有方法 * execution(* com.example.demo.controller..*.*(..)) * 第一个*返回值任意 * com.example.demo.controller..controller包及其所有子包 * 第二个*类名任意 * 第三个*方法名任意 * (..)参数任意 */ Pointcut(execution(public * com.example.demo.controller..*.*(..))) public void webLog() {} /** * 环绕通知 */ Around(webLog()) public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { // 1. 请求开始时间 long startTime System.currentTimeMillis(); // 2. 获取当前HTTP请求对象 ServletRequestAttributes attributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes null) { // 非Web请求上下文直接执行原方法 return joinPoint.proceed(); } HttpServletRequest request attributes.getRequest(); // 3. 生成或获取本次请求的唯一追踪ID并放入MDC String traceId request.getHeader(X-Trace-Id); if (traceId null || traceId.isEmpty()) { traceId UUID.randomUUID().toString().replace(-, ).substring(0, 16); } MDC.put(traceId, traceId); // 关键设置到MDC后续所有日志自动携带 // 4. 构建请求日志信息 String requestLog buildRequestLog(request, joinPoint); LOGGER.info([Request Start] {}, requestLog); Object result null; try { // 5. 执行目标方法 result joinPoint.proceed(); // 6. 计算耗时并记录响应日志 long endTime System.currentTimeMillis(); long costTime endTime - startTime; String responseLog buildResponseLog(result, costTime); LOGGER.info([Request End] Cost: {}ms, Response: {}, costTime, responseLog); return result; } catch (Throwable e) { // 7. 异常处理日志 long endTime System.currentTimeMillis(); long costTime endTime - startTime; LOGGER.error([Request Error] Cost: {}ms, Exception: {}, costTime, e.getMessage(), e); // 异常需要继续抛出让全局异常处理器或上层处理 throw e; } finally { // 8. 最终清理务必清除MDC中的traceId防止内存泄漏和上下文污染 MDC.clear(); } } /** * 构建请求日志字符串 */ private String buildRequestLog(HttpServletRequest request, ProceedingJoinPoint joinPoint) { StringBuilder sb new StringBuilder(); sb.append(TraceId).append(MDC.get(traceId)).append( | ); sb.append(IP).append(getClientIp(request)).append( | ); sb.append(Method).append(request.getMethod()).append( | ); sb.append(URI).append(request.getRequestURI()).append( | ); sb.append(Class.Method).append(joinPoint.getSignature().getDeclaringTypeName()) .append(.).append(joinPoint.getSignature().getName()).append( | ); // 谨慎记录参数敏感信息如密码需要脱敏大参数可能影响性能 Object[] args joinPoint.getArgs(); if (args ! null args.length 0) { try { // 简单示例生产环境建议自定义序列化过滤敏感字段 String params objectMapper.writeValueAsString(args); // 对参数进行脱敏处理示例隐藏密码 params maskSensitiveInfo(params); sb.append(Args).append(params); } catch (Exception e) { sb.append(Args[Serialization Error]); } } else { sb.append(Args[]); } return sb.toString(); } /** * 构建响应日志字符串 */ private String buildResponseLog(Object result, long costTime) { try { String resultStr objectMapper.writeValueAsString(result); // 响应结果也可能需要脱敏或截断 if (resultStr.length() 500) { // 避免日志过长 resultStr resultStr.substring(0, 500) ...(truncated); } return resultStr; } catch (Exception e) { return [Serialization Error]; } } /** * 获取客户端真实IP处理代理情况 */ private String getClientIp(HttpServletRequest request) { String ip request.getHeader(X-Forwarded-For); if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(Proxy-Client-IP); } if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(WL-Proxy-Client-IP); } if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(HTTP_CLIENT_IP); } if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(HTTP_X_FORWARDED_FOR); } if (ip null || ip.length() 0 || unknown.equalsIgnoreCase(ip)) { ip request.getRemoteAddr(); } // 对于通过多个代理的情况第一个IP为客户端真实IP if (ip ! null ip.contains(,)) { ip ip.split(,)[0].trim(); } return ip; } /** * 简单的敏感信息脱敏示例 */ private String maskSensitiveInfo(String jsonStr) { // 这是一个非常简单的示例实际应用中应使用更健壮的方式如注解或配置中心 return jsonStr.replaceAll((?i)\password\\\s*:\\s*\[^\]*\, \password\:\******\) .replaceAll((?i)\token\\\s*:\\s*\[^\]*\, \token\:\******\); } }3.3 日志配置优化光有切面还不够我们需要配置日志框架让输出的日志格式美观且包含我们注入的traceId。在application.yml或logback-spring.xml中配置logging: pattern: console: %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{50} - %msg%n level: com.example.demo.aspect: INFO关键部分是[%X{traceId}]它会从MDCMapped Diagnostic Context中取出我们设置的traceId并打印出来。这样同一个请求下的所有日志包括切面里的、Service里手动打的都会自动带上这个ID用grep traceIdxxx就能轻松串起整个请求链路的所有日志。4. 深入细节那些容易踩坑的“暗礁”代码跑起来不难但想在生产环境稳定运行以下几个坑你必须提前知道。4.1 切点表达式失效为什么我的Controller没被切入这是新手最高频的问题。首先检查你的切面类是否被Spring管理加了Component或Aspect。其次确保你的Controller方法是通过Spring代理调用的。如果一个Controller方法内部调用了另一个Controller方法或者被同一个类内部的其他方法调用由于不是通过代理对象调用AOP会失效。这是Spring AOP基于代理机制的本质决定的。解决方案避免在Controller内部进行方法调用。如果架构上必须可以考虑使用AopContext.currentProxy()来获取当前代理对象然后通过它来调用但这会引入代码耦合不推荐。更优雅的方式是重新审视职责划分将需要AOP增强的逻辑提取到独立的Bean中。4.2 请求体Request Body只能读一次的问题在buildRequestLog方法中我们通过joinPoint.getArgs()获取参数。但如果参数中有RequestBody注解的对象并且你在Controller里已经通过HttpServletRequest.getInputStream()读取了那么在切面里再读可能会报错因为流只能读一次。解决方案使用ContentCachingRequestWrapper。你可以配置一个Filter在最开始将原生的HttpServletRequest包装一下。Component public class CachingRequestBodyFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest new ContentCachingRequestWrapper(request); filterChain.doFilter(wrappedRequest, response); } }然后在切面中判断如果是ContentCachingRequestWrapper就可以通过wrappedRequest.getContentAsByteArray()安全地多次读取请求体。不过要注意这会把整个请求体缓存到内存对于上传大文件等场景需要谨慎评估。4.3 异步线程导致MDC上下文丢失如果你的服务中使用了Async或线程池处理异步任务你会发现子线程里的日志丢失了traceId。因为MDC是基于ThreadLocal实现的线程切换时上下文不会自动传递。解决方案需要手动传递。在提交异步任务前先获取当前的MDC上下文MapString, String context MDC.getCopyOfContextMap(); CompletableFuture.runAsync(() - { // 在子线程开始时设置 if (context ! null) { MDC.setContextMap(context); } try { // 你的异步逻辑 } finally { MDC.clear(); } }, executor);对于Spring的Async可以定义一个AsyncConfigurer配置一个TaskDecorator来包装任务自动完成MDC的传递。4.4 日志性能与磁盘I/O瓶颈全量、详细的日志打印尤其是将大对象序列化为JSON在高并发下会成为性能杀手甚至打满磁盘I/O。优化策略异步日志配置Logback或Log4j2的异步Appender让日志写入操作由独立的线程池完成不阻塞主业务线程。采样打印非核心查询接口可以按比例采样打印日志比如只记录1%的请求详情。条件化日志使用LOGGER.isDebugEnabled()或LOGGER.isInfoEnabled()判断后再进行昂贵的字符串拼接或序列化操作。精简日志内容对于大的列表返回结果只记录条数或摘要而非全部数据。敏感信息和超大参数体要有选择地过滤或截断。4.5 循环依赖与初始化顺序如果你的切面Autowired了某个Bean而这个Bean又间接依赖了被切面拦截的Bean可能会导致循环依赖。或者切面在Spring容器初始化早期就被加载但此时一些它依赖的Bean如ObjectMapper可能还未完全初始化。解决方案尽量避免在切面中注入复杂的Bean。如果必须注入考虑使用Lazy注解进行延迟加载。确保切面本身的逻辑尽可能轻量只做日志记录这一件事。5. 进阶与扩展让日志系统更具价值基础功能实现后我们可以思考如何让它更好地服务于监控和运维。5.1 集成Metrics与告警日志不仅是事后排查的也可以做实时的健康度指标。我们可以在切面中将请求耗时、状态成功/失败等信息推送到时序数据库如Prometheus或监控系统。// 伪代码示例 Around(webLog()) public Object doAroundWithMetrics(ProceedingJoinPoint joinPoint) throws Throwable { String metricName http.request; Timer.Sample sample Timer.start(); boolean isSuccess false; try { Object result joinPoint.proceed(); isSuccess true; return result; } finally { sample.stop(Metrics.timer(metricName, uri, request.getRequestURI(), method, request.getMethod(), status, isSuccess ? success : error)); } }这样你可以在Grafana上绘制接口的P99耗时、QPS、错误率大盘并设置规则当某个接口错误率突增或耗时飙升时自动告警。5.2 基于注解的精细化控制不是所有接口都需要记录详细日志。我们可以自定义一个注解Loggable通过它来控制日志级别、是否记录参数/返回值、是否忽略该接口等。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface Loggable { boolean recordParams() default true; boolean recordResult() default true; Level level() default Level.INFO; }然后在切点表达式中可以结合annotation()来匹配带有该注解的方法实现更灵活的日志策略。5.3 与分布式链路追踪系统集成我们手动生成的traceId是一个简易方案。在生产级的微服务体系中应该集成SkyWalking、Zipkin、Jaeger这样的专业APM应用性能管理工具。它们提供了自动注入的TraceId并且能跨服务、跨进程传递。我们的切面可以调整为优先使用这些工具提供的TraceId并确保日志格式与其兼容实现日志与链路的关联查询。6. 测试与验证如何确保切面按预期工作写完代码不上线测试等于闭着眼睛开车。验证AOP切面我习惯用三层测试法单元测试隔离测试切面逻辑使用SpringBootTest但只加载切面所在的上下文Mock一个HttpServletRequest和ProceedingJoinPoint验证你的buildRequestLog等方法逻辑是否正确特别是脱敏和IP获取逻辑。集成测试验证切面与Controller的编织写一个简单的Test Controller使用MockMvc发起HTTP请求然后断言日志是否被正确打印。这里可以检查日志输出中是否包含了预期的URL、方法、traceId等信息。生产预览测试在预发布环境通过真实的网关或前端调用观察ELKElasticsearch, Logstash, Kibana或你的日志平台上日志格式是否规整traceId是否能成功串联起一个完整请求的多个步骤如网关-服务A-服务B。一个常见的验证点是故意在Controller里抛出一个异常看看错误日志是否被切面的AfterThrowing或Around中的catch块捕获并记录同时确保异常依然向上抛出了没有被“吞掉”。走到这一步你的统一请求日志切面就不再是一个简单的工具类而是一个融入了可观测性思维的、为生产环境准备的基础设施组件。它带来的不仅是开发时的便利更是线上问题定位效率的质变。当你下次凌晨三点被告警电话叫醒能在一分钟内通过traceId定位到问题根因时你会感谢今天花时间搭建了它。

相关新闻

唐诗宋词知识图谱构建完整实施方案

唐诗宋词知识图谱构建完整实施方案

一、项目概述1.1 项目目标要构建出结构化、同时关联化、并且可推理的唐诗宋词领域知识图谱, 还要打通诗人、以及词作、再加上朝代、还有意象、包括典故、涉及地理、有关格律、关乎情感这八大核心维度的关联关系, 最终实现将古典诗词知识予以系统化进行梳理、能够智能检索、可以…

2026/8/7 7:28:18 阅读更多 →
制造业企业文件管理方案选型:从存储架构到AI集成能力的技术视角

制造业企业文件管理方案选型:从存储架构到AI集成能力的技术视角

前言最近在制造业IT交流群里看到一个讨论:一位负责某汽车零部件企业信息化的朋友提到,他们公司图纸版本管理混乱的问题困扰已久——设计部门改完图纸发到生产,生产用的却是旧版本,等发现时已经加工了一批料。群里响应的人不少&…

2026/8/7 7:28:18 阅读更多 →
虚幻引擎Niagara粒子系统性能优化实战指南:从CPU/GPU瓶颈到渲染器选型

虚幻引擎Niagara粒子系统性能优化实战指南:从CPU/GPU瓶颈到渲染器选型

1. 项目概述:为什么Niagara粒子性能优化是技术美术的必修课? 在虚幻引擎项目里,Niagara粒子系统是创造视觉奇观的利器,从漫天飞舞的魔法光点,到爆炸后四散的碎片,再到环境中的雾气尘埃,都离不开…

2026/8/7 7:28:18 阅读更多 →

最新新闻

TVA-VLA架构:具身智能规模化落地关键支撑(5)

TVA-VLA架构:具身智能规模化落地关键支撑(5)

前沿技术探索:TVA智能体(简称TVA)TVA智能体(亦称“TVA视觉智能体”或“AI智能体视觉”)是依托Transformer架构与“因式智能体”理论构建的通用视觉技术框架。它深度融合深度强化学习(DRL)、卷积…

2026/8/7 7:59:35 阅读更多 →
3分钟掌握终极macOS窗口置顶工具,让你的多任务效率提升300%!

3分钟掌握终极macOS窗口置顶工具,让你的多任务效率提升300%!

3分钟掌握终极macOS窗口置顶工具,让你的多任务效率提升300%! 【免费下载链接】Topit Pin any window to the top of your screen / 在Mac上将你的任何窗口强制置顶 项目地址: https://gitcode.com/gh_mirrors/to/Topit 还在为macOS上多个窗口互相…

2026/8/7 7:59:35 阅读更多 →
3.2x 计费系数值不值?Cantus 在真实项目中的 ROI 量化实录

3.2x 计费系数值不值?Cantus 在真实项目中的 ROI 量化实录

3.2x 计费系数值不值?Cantus 在真实项目中的 ROI 量化实录摘要:Qoder Cantus 模型的 Credits 消耗系数为 3.2x,显著高于同平台标准模型。这笔溢价是否合理?本文摒弃主观体验,设计了一套“时间-成本”ROI 测算模型&…

2026/8/7 7:59:35 阅读更多 →
尚硅谷四轴无人机

尚硅谷四轴无人机

四轴无人机四轴无人机简介一、需求说明二、飞行器组成1、飞机部分三、 遥控器部分需求实现思路PID控制算法四轴无人机简介 一、需求说明 1、保持飞行姿态稳定 2、遥控前后左右移动 3、实现定高飞行 二、飞行器组成 1、飞机部分 ①机架采用“X”型飞的更稳 ②动力系统为空心…

2026/8/7 7:59:35 阅读更多 →
从插件到权限系统:AI Agent工具使用的安全管控设计

从插件到权限系统:AI Agent工具使用的安全管控设计

1. 从“插件”到“权限”:一个被误解的核心概念最近和几个做AI应用的朋友聊天,发现一个挺有意思的现象。大家一提到让大模型使用外部工具,比如调用API、查询数据库或者操作文件,第一反应往往是:“哦,就是给…

2026/8/7 7:59:35 阅读更多 →
STranslate:Windows 平台的多引擎划词翻译与 OCR 识别工具

STranslate:Windows 平台的多引擎划词翻译与 OCR 识别工具

STranslate:Windows 平台的多引擎划词翻译与 OCR 识别工具一款专为 Windows 用户设计的开源划词翻译和 OCR 识别工具,支持多种翻译引擎切换,绿色便携无需安装。📖 背景说明 STranslate 是一款面向 Windows 平台的划词翻译与 OCR 文…

2026/8/7 7:58:35 阅读更多 →

日新闻

为什么scrcpy成为Android投屏的终极解决方案:完整实战指南

为什么scrcpy成为Android投屏的终极解决方案:完整实战指南

为什么scrcpy成为Android投屏的终极解决方案:完整实战指南 【免费下载链接】scrcpy Display and control your Android device 项目地址: https://gitcode.com/GitHub_Trending/sc/scrcpy 想要将Android手机屏幕完美投射到电脑上,享受大屏操作的自…

2026/8/7 0:00:19 阅读更多 →
如何在5分钟内掌握Tom Select:打造现代化表单选择器的终极指南

如何在5分钟内掌握Tom Select:打造现代化表单选择器的终极指南

如何在5分钟内掌握Tom Select:打造现代化表单选择器的终极指南 【免费下载链接】tom-select Tom Select is a lightweight (~16kb gzipped) hybrid of a textbox and select box. Forked from selectize.js to provide a framework agnostic autocomplete widget wi…

2026/8/7 0:00:19 阅读更多 →
5分钟快速上手:NSZ压缩工具终极指南,轻松管理Switch游戏文件

5分钟快速上手:NSZ压缩工具终极指南,轻松管理Switch游戏文件

5分钟快速上手:NSZ压缩工具终极指南,轻松管理Switch游戏文件 【免费下载链接】nsz NSZ - Homebrew compatible NSP/XCI compressor/decompressor 项目地址: https://gitcode.com/gh_mirrors/ns/nsz 你是否在为Nintendo Switch游戏文件占用大量存储…

2026/8/7 0:00:19 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/6 22:02:27 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/6 22:02:27 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/6 22:02:27 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/5 23:28:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/6 22:02:28 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/5 23:46:51 阅读更多 →