在spring中应使用@aspect切面而非handlerinterceptor实现跨层方法日志记录,因后者仅适用于controller层;需定义@logexecution注解并编写切面类,在目标方法上标注该注解即可自动记录执行耗时、参数与结果。

在 Spring 项目中,可以通过自定义注解 + 拦截器(或更常用的是 AOP 切面)实现对特定方法的自动日志记录。注意:Spring 的 HandlerInterceptor 主要用于 Web 层(Controller 方法),无法拦截 Service 或普通 Bean 中的方法;若需拦截任意 Spring 管理的 Bean 方法(如 @Service、@Component 内的方法),应使用 @Aspect 切面。
1. 定义自定义日志注解
创建一个运行时保留、可作用于方法的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogExecution {
String value() default "执行日志";
boolean includeParams() default true;
boolean includeResult() default true;
}2. 编写 AOP 切面处理日志逻辑
使用 @Aspect 拦截标注了 @LogExecution 的方法,记录入参、耗时、返回值或异常:
@Aspect
@Component
@Slf4j
public class LogExecutionAspect {
<pre class="brush:java;toolbar:false;">@Around("@annotation(logExecution)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecution logExecution) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
long start = System.currentTimeMillis();
String params = logExecution.includeParams() ? Arrays.toString(args) : "[hidden]";
log.info("▶ {} 开始执行 | 参数: {}", methodName, params);
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
String resultStr = logExecution.includeResult()
? String.valueOf(result)
: "[hidden]";
log.info("✔ {} 执行完成 | 耗时: {}ms | 返回: {}", methodName, duration, resultStr);
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - start;
log.error("✘ {} 执行异常 | 耗时: {}ms | 异常: {}", methodName, duration, e.getMessage(), e);
throw e;
}
}}
- 确保类被
@Component管理,并开启 AOP 支持(@EnableAspectJAutoProxy或 spring-boot-starter-aop 已默认启用) - 使用
@Slf4j(Lombok)简化日志;也可用LoggerFactory.getLogger(...) -
joinPoint.proceed()是关键,必须调用才能真正执行原方法
3. 在目标方法上使用注解
只需在需要记录日志的方法上添加 @LogExecution:
@Service
public class UserService {
<pre class="brush:java;toolbar:false;">@LogExecution(includeParams = true, includeResult = false)
public User getUserById(Long id) {
return new User(id, "Alice");
}}
- 支持细粒度控制:是否打印参数、是否打印返回值
- 注解可重复使用,无需修改业务代码逻辑
4. 补充说明:为什么不用 HandlerInterceptor?
HandlerInterceptor 只能拦截进入 DispatcherServlet 的 HTTP 请求,即 Controller 层方法。它无法感知 Service、DAO 或普通工具方法的调用。若强行在 Controller 中加拦截,会丢失内部方法调用链路,且耦合 Web 层。
- Controller 方法 → 拦截器 ✅
- Service 方法 → AOP 切面 ✅(推荐)
- 非 Spring 管理对象的方法 → AOP 不生效,需改用字节码增强(如 ByteBuddy)或手动埋点
不复杂但容易忽略:确保切面类被 Spring 扫描到(放在主启动类同包或子包),且项目已引入 spring-boot-starter-aop 依赖。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











