Spring基于注解的环绕通知实现请求方法日志记录

  • Post author:
  • Post category:其他


使用注解AOP实现方法日志记录,免去重复写日志存储的麻烦。该方案能记录方法请求参数,返回结果等信息

代码结构:

1.自定义注解

package com.example.demo.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 在方法上使用该注解时,Spring会自动存储被注解方法的请求参数和返回信息到log表
 * 存储的基础字段包括:
 *      businessItemId:方法名称
 *      requestParameter:请求的参数名称以及参数值
 *      responseContent:请求的返回
 *      result:如果程序正常运行则存储success,否则存储failure
 *      types:传参中的logType(根据注解中的传参决定,非必填)
 *      typesName:types对应的名称
 *      businessId:传参中的businessId字段(根据注解中的传参决定,非必填)
 *      createTime:创建时间
 *      updateTime:更新时间
 *      note:如果程序异常执行,note字段存储e.getMessage()
 * @author shengsheng
 */
// 方法注解
@Target(ElementType.METHOD)
// 运行时可见
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnno {
    /**
     * log的types字段
     * @return
     */
    String logType() default "";

    /**
     * log的businessId字段
     * @return
     */
    String businessId() default "";
}

2.切面实现

package com.example.demo.aop;

import com.alibaba.fastjson.JSON;
import com.example.demo.entity.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Log的AOP实现日志
 * @author shengsheng
 *
 */
@Component
@Aspect
public class LogAspect {

    /*@Autowired
    private LogMapper logMapper;*/

    @Pointcut("@annotation(com.example.demo.aop.LogAnno)")
    public void logPointCut() {
    }

    /**
     * 环绕通知记录日志通过注解匹配到需要增加日志功能的方法
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        Log log = new Log();
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        // 1.方法执行前的处理,相当于前置通知
       // 获取正在访问的类
        Class executionClass = pjp.getTarget().getClass();
        // 获取访问的方法的名称
        String methodName = pjp.getSignature().getName();
        // 获取正在访问的方法
        Method executionMethod =methodSignature.getMethod();
        // 获取访问的方法的参数
        Object[] args = pjp.getArgs();
        //获取方法中的参数名称
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        String[] parameterNames = pnd.getParameterNames(executionMethod);
        log.setBusinessItemId(methodName);
        if((parameterNames!=null)&&(parameterNames.length>0)&&(args!=null)&&(args.length>0)){
            Map<String,Object> requestParams = new HashMap<>(args.length);
            for (int i = 0 ;i<parameterNames.length;i++) {
                requestParams.put(parameterNames[i],args[i]);
            }
            //设置请求参数
            log.setRequestParameter(JSON.toJSONString(requestParams));
        }
        LogAnno annotation = executionMethod.getAnnotation(LogAnno.class);
        //获取注解中的types字段
        String logTypeStr = annotation.logType();
        if(!StringUtils.isEmpty(logTypeStr)){
            Integer logType = Integer.valueOf(logTypeStr);
            log.setTypes(logType);
            log.setTypesName("");
        }
        String businessId = annotation.businessId();
        log.setBusinessId(businessId);
        Object result = null;
        try {
            //让代理方法执行
            result = pjp.proceed();
            // 2.后置通知
            log.setResult("success");
        } catch (Exception e) {
            // 3.异常通知
            log.setResult("failure");
            log.setNote(e.getMessage());
        } finally {
            // 4.最终通知
            Date now = new Date();
            log.setCreateTime(now);
            log.setResponseContent(JSON.toJSONString(result));
            log.setUpdateTime(now);
            System.out.println(log);
            //入库
//            logMapper.insertSelective(log);
        }
        return result;
    }
}

3.在方法上使用注解

package com.example.demo.utils;

import com.example.demo.aop.LogAnno;
import org.springframework.stereotype.Component;

/**
 * description:
 *
 * @author shengsheng
 * @date 2020/9/24 16:32
 */
@Component
public class CalculateUtils {

    //使用注解
    @LogAnno(logType = "1",businessId = "div")
    public Integer div(Integer a,Integer b){
        return a/b;
    }
}

4.测试

package com.example.demo.test;

import com.example.demo.utils.CalculateUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * description:
 *
 * @author shengsheng
 * @date 2020/9/24 16:48
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class CalculateTest {
    @Autowired
    private CalculateUtils calculateUtils;
    @Test
    public void test() {
        Integer div = calculateUtils.div(1, 1);
        System.out.println(div);
    }
}

5.测试结果

Log{id=null, types=1, typesName='', businessId='div', businessItemId='div', result='success', requestParameter='{"a":1,"b":1}', responseContent='1', note='null', createTime=Thu Sep 24 17:12:57 CST 2020, updateTime=Thu Sep 24 17:12:57 CST 2020}



版权声明:本文为shengshenglalalala原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。