spring中的切面类实践

目标功能:自定义一个在方法执行前后做日志记录的切面方法,需要考虑到方法执行成功与失败的情况,并记录方法名与意义与参数。

自定义的切面类需要配套一个自定义注解执行

java 复制代码
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface LogRec {
    String value() default ""; // 接口名称描述
}

切面类的类层面

java 复制代码
@Aspect
@Component
public class LogRecordAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);

    @Autowired
    private SystemService systemService;

    @Pointcut("@annotation(com.example.config.aspect.LogRec)")
    public void logRecMethods() { }
    ...
}

类里面环绕目标方法的增强方法

java 复制代码
    /**
     * 环绕通知并记录日志
     * @param joinPoint
     * @param logRec
     * @return
     * @throws Throwable
     */
    @Around("logRecMethods() && @annotation(logRec)")
    public Object logAround(ProceedingJoinPoint joinPoint, LogRec logRec) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 获取注解值
        String methodDesc = logRec.value();
        Object result = null;
        Object logRes = null;

        try {
            // 获取 request
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            // 执行原方法
            result = joinPoint.proceed();
            logRes = result;
            //logRes可以用来做额外处理,保留最终返回的result的完整性,避免误修改
            //正常日志记录
            ...
            return result;
        } catch (Exception e) {
            //异常日志记录
            logger.error("接口调用异常:{}", e.getMessage(), e);
            throw e;
        } finally {
            //收尾代码
            ...
        }
    }

起初考虑不全时,只想到before和after,这里也记录一下。特别提示:这俩和around不能共存。

java 复制代码
    @Before("logRecMethods() && @annotation(logRec)")
    public void logBefore(JoinPoint joinPoint,LogRec logRec){
        String methodName = joinPoint.getSignature().getName();
        String methodParams = logRec.value();
        String interfaceName = joinPoint.getTarget().getClass().getName();
        logger.info("【"+interfaceName+"】"+methodName+"("+methodParams+")");
        Object[] args = joinPoint.getArgs();
        for (Object arg : args){
            if (arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                systemService.writeReqLog(request,methodParams+":开始执行");
            }
        }
    }
java 复制代码
    @After("logRecMethods() && @annotation(logRec)")
    public void logAfter(JoinPoint joinPoint,LogRec logRec){
        String methodName = joinPoint.getSignature().getName();
        String methodParams = logRec.value();;
        String interfaceName = joinPoint.getTarget().getClass().getName();
        logger.info("【"+interfaceName+"】"+methodName+"("+methodParams+")");
        Object[] args = joinPoint.getArgs();
        for (Object arg : args){
            if (arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                systemService.writeReqLog(request,methodParams+":结束执行");
            }
        }
    }

除了环绕方法,还有出现异常时的处理方法

java 复制代码
    /**
     * 异常通知:记录失败日志
     */
    @AfterThrowing(pointcut = "logRecMethods()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        String methodParams = ex.getMessage();
        String interfaceName = joinPoint.getTarget().getClass().getName();
        logger.info("【"+interfaceName+"】"+methodName+"("+methodParams+")");
        Object[] args = joinPoint.getArgs();
        for (Object arg : args){
            if (arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                systemService.writeReqLog(request,"发生异常:"+ex.getMessage());
            }
        }
    }
相关推荐
Moment2 小时前
长上下文会最终杀死 Rag 吗?
前端·javascript·后端
蝎子莱莱爱打怪2 小时前
🚀 🚀🚀2026年5月GitHub月榜精选:17个项目中挑出10个推荐,实操4个!
人工智能·后端·ai编程
砍材农夫3 小时前
物联网实战:Spring Boot MQTT | MQTT 设备模拟器演示(附源码)
java·spring boot·后端·物联网·spring·netty
我叫黑大帅4 小时前
解决聊天页内部滚轮改为页面滚动问题
javascript·后端·面试
IT_陈寒4 小时前
Python的线程池居然把我坑在了垃圾回收这块
前端·人工智能·后端
zhangxingchao5 小时前
AI应用开发八:RAG相关技术总结
前端·人工智能·后端
吴佳浩5 小时前
Go史上最大“打脸”现场来了:泛型方法终于实现了
后端·go
Huyuejia5 小时前
runtime-ask
后端
Rust研习社5 小时前
90% 的 Rust 新手都不知道的 3 个实用开发技巧
后端·rust·编程语言
ZengLiangYi6 小时前
sql.js WASM 深度解析
javascript·数据库·后端