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());
            }
        }
    }
相关推荐
UrbanJazzerati11 分钟前
AutoHotkey v2 零基础快速入门 Part 10:给路线回放添加高精度计时,理解累计延迟与路线一致性
后端
70后写java的老码农15 分钟前
Spring Boot 3 + ShardingSphere-JDBC 实战:一次把读写分离和分库分表跑通
后端
泡海椒18 分钟前
04-饼图评级构成-信用评级构成
后端
知守观20 分钟前
多模块架构演进:横向分层 vs 纵向领域,13 个真实项目的切分对照
java·后端·微服务
系统集成-鹰翔1 小时前
ERP推单数据库怎么备份:SQLite WAL模式下只复制db文件为什么可能漏任务
javascript·数据库·经验分享·后端
软件技术NINI2 小时前
基于SpringBoot和Vue的摇滚乐鉴赏网站
vue.js·spring boot·后端·spring·html·毕业设计·毕设
Tsuki_tl3 小时前
【总结】Redis五大数据类型应用场景及基本命令
数据库·redis·后端·缓存·缓存中间件·redis命令·redis 五大数据类型
后台模板学习3 小时前
Go Web开发中任务调度框架的源码解析与高可用实现方案
开发语言·后端·golang
系统集成-鹰翔3 小时前
ERP对接升级提示数据库迁移失败:SQLite逐版本事务到底回滚到哪一步
javascript·数据库·经验分享·后端
IT_陈寒3 小时前
深入理解SpringBoot:核心原理与最佳实践
前端·人工智能·后端