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());
            }
        }
    }
相关推荐
东风破_7 小时前
从输入 juejin.cn 到看到页面:DNS、Nginx、服务器集群与 CDN 到底在做什么?
后端·mysql
东风破_7 小时前
从 0 设计一个博客数据库:用户、头像与文章表应该怎么建?
后端·mysql
大鸡腿同学8 小时前
黄仁勋点透的 AI 真相:能实现你知道但做不到的,却实现不了你不知道的
后端
FfHUCisI8 小时前
Go 编译过程全景
开发语言·后端·golang
FfHUCisI8 小时前
Golang 语法分析与 AST:Parser 与 go/ast
开发语言·后端·golang
++==10 小时前
RESTful详解:核心思想、框架、与HTTP的区别,API的设计风格
后端·http·restful
IT_陈寒11 小时前
Vue的响应式更新有时候真的不听话
前端·人工智能·后端
向生12 小时前
Ubuntu Caddy 保姆级完整教程
后端
tntxia12 小时前
Docker权限的问题
后端
明月_清风12 小时前
看完这段关于"全插件化架构"的技术分析后,我整理了一份笔记
前端·后端