spring aop 记录controller层的入参和出参

日志的重要性在项目中不言而喻, 但是对controller层的出入参一个一个的添加, 将是一个比较枯燥的事情, 可以考虑使用aop来记录. 

spring项目中的全局异常处理有两种模式,其一是实现HandlerExceptionResolver接口的模式, 其二是使用@RestControllerAdvice的模式

之前使用springmvc时写过一个aop记录日志的功能(详见: https://github.com/zonaChang/spring-aop-log.git), 最近项目使用的是springboot+@RestControllerAdvice的模式, 之前的方式不适合当前环境, 虽有该文章. 

简述思路:

1. 使用aop将controller层的出入参记录打印出来

2. 为了便于观察, 使用环绕通知将出入参打印在一条记录里面(也可以使用前置,后置,异常通知+ThreadLocal将出入参添加到一条记录)

3. 不仅要记录可以正常执行的请求, 对于异常请求,也应该将其记录下来(该方式暂有一个问题未解决, 如果是controller的参数类型解析错误(如: controller需要一个integer类型的, 但入参为一个字符串), 则不能记录具体的入参. 有能解决的烦请指教, 可以考虑下dispatchServlet调度方面涉及到的组件 )

 

aop代码:
 


import com.carl.study.comm.exception.ExceptionControllerAdvice;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * @author changez
 * @desc 记录日志
 * @datetime 2019/7/20 9:40
 */
@Slf4j
@Component
@Aspect
public class LogAop {


    /**
     * 全局异常处理类全限定名
     */
    private String exceptionAdviceName = ExceptionControllerAdvice.class.getName();
    private static ThreadLocal<StringBuilder> threadLocal = new ThreadLocal<>();

    // @ControllerAdvice 环绕通知,出异常后获取到的参数信息为异常处理的参数信息, 所以暂时不使用环绕通知
    // || execution(* com.bz.wes.org.comm.exception.ExceptionControllerAdvice.*(..))
    @Pointcut("execution(* com.carl.study.*.controller..*.*(..)) || execution(* com.carl.study.exception.ExceptionControllerAdvice.*(..))")
    public void logAroundPointCut(){}

    /**
     * controller层入参日志记录
     */
    @Around("logAroundPointCut()")
    public Object intoControllerLog(ProceedingJoinPoint point) throws Throwable {

        // 目标方法所在类全限定名
        String targetName = point.getSignature().getDeclaringType().getName();
        // 目标方法签名
        StringBuilder sb = null ;
        if (targetName.equals(exceptionAdviceName)) {
            // 异常处理则从threadLocal中取出入参
            sb = threadLocal.get();
            // controller参数解析异常不会进入目标方法, 而直接请求异常处理器
            sb = sb == null ? new StringBuilder(300).append("请求异常, 未进入controller ") : sb;
        } else {
            // 调用目标方法, 将参数存入到threadLocal,  便于发生异常时取到请求参数
            sb = new StringBuilder(300);
            // 类名+方法名
            String target = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
            Object[] args = point.getArgs();
            String[] paramsName =  ((MethodSignature) point.getSignature()).getParameterNames();

            sb.append(target).append(" 入参:【");
            if(args != null && paramsName != null && args.length > 0 && paramsName.length > 0) {
                for (int i=0; i< paramsName.length; i++) {
                    sb.append(" ").append(paramsName[i]).append(" = ").append(args[i]).append(",");
                }
                sb.deleteCharAt(sb.length()-1);
            }
            sb.append("】");
            threadLocal.set(sb);
        }

        // 调用目标方法
        Object result = point.proceed();
        sb.append(" 出参:【").append(result).append("】");
        // 记录日志
        log.info(sb.toString());
        // 调用结果返回
        return result;
    }
}

 

全局异常处理:

这种模式下, 请求出现异常后, 类似于产生了一个新的请求来调用相应的异常处理器, 此时在aop中将获取的是该异常处理器的入参, 而非原始的请求入参(在aop中使用threadLocal解决该问题)

import com.carl.study.comm.response.JsonResponse;
import com.carl.study.comm.response.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
@Slf4j
public class ExceptionControllerAdvice {

    /**
     * 拦截参数异常
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    public MetaRestResponse myErrorHandler(IllegalArgumentException e) {
        log.error(">>>拦截参数异常>>" + e.getMessage());
        JsonResponse result = JsonResponse.error(ResponseCode.BAD_REQUEST, e.getMessage());
        return result;
    }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值