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());
            }
        }
    }
相关推荐
麦兜*8 分钟前
Spring Boot 整合 Spring Data JPA 入门:只需注解,告别 SQL
spring boot·后端·sql
Sally璐璐10 分钟前
RESTful与RPC接口终极对比指南
后端·rpc·restful
J_liaty42 分钟前
前后端跨域处理全指南:Java后端+Vue前端完整解决方案
java·前端·vue.js·spring boot·后端
颜淡慕潇44 分钟前
深度解读 Spring Boot 3.5.9— 工程视角的稳健演进与价值释放
java·spring boot·后端·spring
玄〤1 小时前
黑马点评中的分布式锁设计与实现(Redis + Redisson)
java·数据库·redis·笔记·分布式·后端
码界奇点1 小时前
基于SpringBoot与Shiro的细粒度动态权限管理系统设计与实现
java·spring boot·后端·spring·毕业设计·源代码管理
摸鱼的春哥1 小时前
继续AI编排实战:带截图的连麦切片文章生成
前端·javascript·后端
Yuer20251 小时前
状态不是变量:Rust 量化算子中的 State 工程语义
开发语言·后端·深度学习·机器学习·rust
野犬寒鸦1 小时前
从零起步学习RabbitMQ || 第二章:RabbitMQ 深入理解概念 Producer、Consumer、Exchange、Queue 与企业实战案例
java·服务器·数据库·分布式·后端·rabbitmq
IT_陈寒1 小时前
Vite 4.0实战:5个被低估的配置项让构建速度提升50%
前端·人工智能·后端