问题现象
接口请求日志显示耗时仅为几十毫秒,但通过Postman发起相同请求时,实际耗时却高达7秒多。
bash
[2025-12-02 09:06:27.344]-[INFO]-[http-nio-3012-exec-3]-[logId:6600b2f0-8d68-4c2c-a7b5-bce11a42e140]-[com.xxx.interceptor.ControllerInterceptor.info:63] 调用 接口://task/retry Class:com.xxx.xxx.xxx.controller.TaskController,Method:taskRetry,Request:101
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@63bd0ef2] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@3448c914] will not be xxxd by Spring
==> Preparing: select id, task_code, task_name, task_desc, task_execute_time, task_over_time, task_timer, file_path, status, task_type, create_name, update_name, create_date, update_date from r_task where id = ? and status = ?
==> Parameters: 101(Integer), 4(Byte)
<== Total: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@63bd0ef2]
[2025-12-02 09:06:27.354]-[WARN]-[http-nio-3012-exec-3]-[logId:6600b2f0-8d68-4c2c-a7b5-bce11a42e140]-[com.xxx..xxx.service.impl.RetryTaskServiceImpl.taskRetry:50]-任务重试任务查询为空,id:101
[2025-12-02 09:06:27.355]-[INFO]-[http-nio-3012-exec-3]-[logId:6600b2f0-8d68-4c2c-a7b5-bce11a42e140]-[com.xxx.interceptor.ControllerInterceptor.logReturn:81]-Class:com.xxx..xxx.controller.TaskController,Method:taskRetry,Response:{"code":"999","msg":"任务重试失败"}
postman请求示例

问题排查
先用arthas的 trace命令 确定哪块执行比较耗时
bash
`---[6792.636ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:intercept()
+---[0.0061ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:getTarget() #655
+---[0.005ms] org.springframework.aop.framework.AdvisedSupport:getInterceptorsAndDynamicInterceptionAdvice() #659
+---[0.0079ms] org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation:<init>() #673
+---[6792.5681ms] org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation:proceed() #673
| `---[10.2326ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:intercept()
| +---[0.0044ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:getTarget() #655
| +---[0.0047ms] org.springframework.aop.framework.AdvisedSupport:getInterceptorsAndDynamicInterceptionAdvice() #659
| +---[0.0092ms] org.springframework.aop.framework.AopProxyUtils:adaptArgumentsIfNecessary() #668
| +---[10.1483ms] org.springframework.cglib.proxy.MethodProxy:invoke() #669
| +---[0.0034ms] org.springframework.aop.framework.CglibAopProxy:access$000() #675
| `---[0.0031ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:releaseTarget() #680
+---[0.0034ms] org.springframework.aop.framework.CglibAopProxy:access$000() #675
`---[0.0025ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:releaseTarget() #680
经过断点调试,程序最终进入了自定义的ControllerInterceptor中的logBefore方法。该方法的主要逻辑相对简单,主要用于打印请求日志并对输入参数进行脱敏处理。问题出在负责参数脱敏的SensitiveInfoSerialize.getJson方法上。
通过断点跟踪发现,程序是在MethodBeforeAdviceInterceptor中执行this.advice.before这行代码时进入自定义ControllerInterceptor的。
java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
}
ControllerInterceptor 的logBefore
java
/**
* /**
* 记录输入日志
*
* @param joinPoint
*/
@Before(value = "controllerService()")
public void logBefore(JoinPoint joinPoint) {
LogUtil.bindLogId(UUID.randomUUID().toString());
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
if (ArrayUtils.isNotEmpty(args)) {
LogHelper.info(logger, "调用", "接口:" + logTool.getServiceKey(), "Class:" + className
+ ",Method:" + methodName + ",Request:" + SensitiveInfoSerialize.getJson(args[0]), "", "", "");
}
}
这段代码的核心逻辑是:
- 遍历 Java Bean 的所有字段
- 识别带有 SensitiveInfo 注解的 String 类型字段
- 对这些字段执行脱敏处理
- 对嵌套对象、数组、集合和 Map 进行递归处理
需要注意的是,这是一个内部工具类,虽然具体实现不便展示,但实际使用中存在一些需要注意的问题。
结论
根本原因是对参数进行递归的时候没有排除基本数据类型,导致对基本数据类型的字段进行递归导致耗时很长。
所以使用递归时要特别注意退出条件,不然可能会导致耗时很长甚至栈溢出。