
文章目录一、 三种方案的代码演练1. AOPAspect 面向切面方案 —— 针对 Controller 方法最推荐2. InterceptorSpring 拦截器方案 —— 针对 Spring MVC 链路3. FilterServlet 过滤器方案 —— 针对最外层 HTTP 请求二、 面试官角度三种方案的本质区别核心考点三、 复盘你在面试中的回答到底对不对1. 得分点说得对的地方四、 下一次面试如何给出“满分回答”根据你提供的面试复盘音频记录面试官在18:29问到了你这个问题“如果说在代码中如果要实现接口的参数耗时接口花了多长时间日志的打印你会去怎么做”首先给你打个气你提到了AOP面向切面编程并在前后获取时间戳相减这个核心思路是完全正确的但是你在细节表达上有些含糊比如提到了“ConcurrentMedia”这个不存在的词 可能是想说System.currentTimeMillis()或者是ProceedingJoinPoint而且没有展开具体的落地方案。面试官最后也建议你“可以调整一下自己的思路写出来比说要好一点” 。为了帮你完美复盘并准备接下来的面试下面我用Spring Boot 的三种主流拦截机制AOP、Interceptor、Filter分别为你演示怎么实现这个功能并客观评估你当时的面试回答。一、 三种方案的代码演练在 Spring Boot 中统计接口参数和耗时通常有以下三种做法。我们由浅入深从你提到的AOP开始1. AOPAspect 面向切面方案 —— 针对 Controller 方法最推荐AOP 能够直接获取到 Spring 解析后的Java 对象入参最适合用来做复杂的日志审计和耗时统计。AspectComponentSlf4jpublicclassLogAspect{ResourceprivateObjectMapperobjectMapper;// 用于序列化参数// 拦截所有 controller 包下的方法Pointcut(execution(public * com.example.demo.controller..*.*(..)))publicvoidcontrollerLog(){}Around(controllerLog())publicObjectdoAround(ProceedingJoinPointjoinPoint)throwsThrowable{longstartTimeSystem.currentTimeMillis();// 记录开始时间// 获取方法名StringmethodNamejoinPoint.getSignature().getName();// 获取入参对象数组Object[]argsjoinPoint.getArgs();log.info(【AOP日志】进入方法: {}, 参数: {},methodName,objectMapper.writeValueAsString(args));// 执行核心业务方法ObjectresultjoinPoint.proceed();longexecutionTimeSystem.currentTimeMillis()-startTime;// 计算耗时log.info(【AOP日志】退出方法: {}, 耗时: {}ms,methodName,executionTime);returnresult;}}2. InterceptorSpring 拦截器方案 —— 针对 Spring MVC 链路拦截器属于 Spring MVC 级别可以在请求到达 Controller 之前和之后进行拦截非常适合配合ThreadLocal统计耗时。ComponentSlf4jpublicclassLogInterceptorimplementsHandlerInterceptor{// 使用 ThreadLocal 保证多线程环境下线程安全地记录各自请求的开始时间privatestaticfinalThreadLocalLongTIME_THREAD_LOCALnewThreadLocal();OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{longstartTimeSystem.currentTimeMillis();TIME_THREAD_LOCAL.set(startTime);// 存入当前线程// 打印基础请求参数URL参数log.info(【拦截器】请求 URI: {}, Method: {}, 参数: {},request.getRequestURI(),request.getMethod(),request.getParameterMap());returntrue;// 放行}OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{LongstartTimeTIME_THREAD_LOCAL.get();if(startTime!null){longendTimeSystem.currentTimeMillis();log.info(【拦截器】请求 URI: {} 处理完成总耗时: {}ms,request.getRequestURI(),(endTime-startTime));TIME_THREAD_LOCAL.remove();// 极其重要用完必须释放防止内存泄漏}}}3. FilterServlet 过滤器方案 —— 针对最外层 HTTP 请求过滤器属于标准 Servlet 容器级别如 Tomcat是全系统最外层的防线。它的生命周期甚至比 Spring 还要早。ComponentWebFilter(urlPatterns/*)Slf4jpublicclassLogFilterimplementsFilter{OverridepublicvoiddoFilter(ServletRequestcontext,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequestrequest(HttpServletRequest)context;longstartTimeSystem.currentTimeMillis();log.info(【过滤器】开始拦截请求: {},request.getRequestURI());// 放行让请求继续往后走走拦截器、Controller等chain.doFilter(context,response);longexecutionTimeSystem.currentTimeMillis()-startTime;log.info(【过滤器】请求: {} 结束耗时: {}ms,request.getRequestURI(),executionTime);}}二、 面试官角度三种方案的本质区别核心考点如果你能在面试中主动说出下面这张表对比面试官会觉得你的计算机网络和 Spring 底层功底极其扎实维度Filter (过滤器)Interceptor (拦截器)AOP (切面)所属框架Servlet 容器规范TomcatSpring MVC 框架Spring Core 框架拦截边界HTTP 请求的最外层入口进入 SpringMVC 但在 Controller 之前具体的某一个方法Method获取入参的便利度极其困难。只能拿到原始 Http 字节流如果读了RequestBody后面业务就读不到了。中等。能拿到request.getParameter()但同样存在 Body 只能读一次的问题。极其轻松。Spring 已经帮我们把参数反序列化成了Java 对象joinPoint.getArgs()。核心应用场景全局跨域CORS、安全过滤XSS防注入、全局解密。权限校验鉴权、TraceId 注入。性能监控统计耗时、事务控制、操作审计日志。三、 复盘你在面试中的回答到底对不对针对你当时的现场表现我为你做一个客观的“诊断报告”1. 得分点说得对的地方思路切中要害你第一时间回答了“可以用 AOP 去做” 并且提到了around环绕通知还知道是在方法执行前后分别获取时间做减法 。在逻辑闭环上是完全成立的。拓展知识面广你顺带提到了可以使用Arthas阿尔萨斯说明你具备线上排查慢接口的实际工具经验。失分点导致你最后“心虚”和减分的地方专业术语口误致命伤你在描述时间戳或者连接点时连续说了两次“获取一个 ConcurrentMedia”。Java 和 Spring 里完全没有这个类你可能脑子里想的是ConcurrentHashMap或者System.currentTimeMillis()的杂交语。这会让面试官觉得你只是背了概念没有真正写过代码。缺乏落地方案当面试官追问“有哪几种方式”以及“你会拦截哪一层”时 你的语言开始细碎重复“就是那回事嘛……”这种口头禅容易显得不自信 。你没有清晰地划分出Filter - Interceptor - AOP这一条清晰的拦截链路。四、 下一次面试如何给出“满分回答”如果下次再被问到“Spring Boot 里怎么统计接口参数和耗时并打印日志”️你应该这样优雅地回答“在 Spring Boot 中通常有Filter、Interceptor 和 AOP三种方式可以拦截请求并统计耗时。在实际生产中对于‘统计接口入参和耗时’这个特定需求我个人最推荐使用 Spring AOP 结合Around环绕通知来实现。因为如果使用 Filter 或 Interceptor由于 HTTP 请求的RequestBody输入流只能读取一次如果我们为了打印日志强制去读取它就会导致后续的 Controller 无法正常解析参数即流关闭异常。而使用AOP由于它切入的是方法级别我们能通过ProceedingJoinPoint.getArgs()直接拿到 Spring 已经反序列化好的 Java 对象入参对核心业务完全做到零侵入。我的落地做法是在环绕通知中先通过System.currentTimeMillis()记录当前时间并利用objectMapper将入参对象转为 JSON 字符串打印出来注意要过滤掉类似HttpServletRequest这种无法序列化的内置对象。接着调用joinPoint.proceed()执行业务最后再次获取时间戳相减算出方法总耗时。此外为了方便线上监控我通常会在代码里设定一个慢响应阈值比如 3 秒。一旦耗时超过 3 秒日志级别会从INFO提升为WARN并在日志头部打上【接口慢响应警告】标签方便后续我们在 ELK 日志平台中通过脚本直接拉取和报警。”