基于注解的AOP日志记录

设计一个基于注解的AOP日志记录功能

1.新建一个自定义注解OperatorLog.java


import java.lang.annotation.*;

/**
 * 自定义注解
 */
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface OperatorLog {
	String value() default "";
}

2.编写AOP日志记录SysLogAspect.java



import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.base.controller.base.BaseController;
import com.base.repository.system.entity.OperatorLogEntity;
import com.base.service.system.IOperatorLogService;
import com.base.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lzh
 * @date 2019/8/5 - 17:25
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect  extends BaseController {

    @Autowired
    private IOperatorLogService operatorLogService;


    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation( com.ysten.longquan.base.aspect.OperatorLog)")
    public void logPoinCut() {
    }


    /**
     * 计算接口请求耗时,要在Around环绕通知里边计算
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("logPoinCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        OperatorLogEntity operatorLogEntity = new OperatorLogEntity();

        //1、开始时间
        long beginTime = System.currentTimeMillis();
        //利用RequestContextHolder获取requst对象
        ServletRequestAttributes requestAttr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest  request = requestAttr.getRequest();
        //请求URL
        String uri = requestAttr.getRequest().getRequestURI();
        operatorLogEntity.setRequestUrl(uri);
        log.debug("URI: {}",uri);

        //访问目标方法的参数 可动态改变参数值
        Object[] args = joinPoint.getArgs();
        //方法名获取
        String methodName = joinPoint.getSignature().getName();
        log.debug("请求方法:{}, 请求参数: {}", methodName, Arrays.toString(args));
        //ip
        log.debug("请求ip:{}", IpUtils.getIpAddr(request));
        operatorLogEntity.setIp(IpUtils.getIpAddr(request));

        //记录请求类型GET PUT POST DELETE
        String httpMethod = request.getMethod();
        operatorLogEntity.setRequestType(httpMethod);

        Signature signature = joinPoint.getSignature();
        if(!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("暂不支持非方法注解");
        }
        //调用实际方法
        Object object = joinPoint.proceed();
        //获取执行的方法
        MethodSignature methodSign = (MethodSignature) signature;
        Method method = methodSign.getMethod();
        //判断是否包含了 无需记录日志的方法
        OperatorLog logAnno = AnnotationUtils.getAnnotation(method, OperatorLog.class);
        if(logAnno != null ) {
            log.debug("log注解描述:{}", logAnno.value());
            operatorLogEntity.setName(logAnno.value());

            //日志细分为get/post/put/delete
            Object[] reqArgs = joinPoint.getArgs();
            String queryString = request.getQueryString();
            String params = "";
            //获取请求参数集合并进行遍历拼接
            if(args.length>0){
                if("POST".equals(httpMethod)){
                    Object obj = args[0];
                    Map map = getKeyAndValue(object);
                    params = JSON.toJSONString(map);

                }else if("GET".equals(httpMethod)){
                    params = queryString;
                }else if ("PUT".equals(httpMethod)){
                    Object obj = args[0];
                    Map map = getKeyAndValue(object);
                    params = JSON.toJSONString(map);
                }else if ("DELETE".equals(httpMethod)){
                    params = queryString;


                }
            }
            //参数保存
            operatorLogEntity.setRequestParam(params);


            //日志创建日期
            operatorLogEntity.setCreateTime(new Date());

            //记录操作用户名
            operatorLogEntity.setUsername(getUserName());
            operatorLogEntity.setCreateBy(getUserName());
            operatorLogEntity.setUpdateBy(getUserName());


            // result的值就是被拦截方法的返回值
            Object result = joinPoint.proceed();
            operatorLogEntity.setResponse(JSONObject.toJSONString(result));
            log.debug("请求返回值:" + JSONObject.toJSONString(result));

            //计算耗时
            long endTime = System.currentTimeMillis();
            long  costTime = endTime - beginTime;
            operatorLogEntity.setCostTime(costTime);
            log.debug("结束计时: {},  URI: {},耗时:{}", new Date(),uri,costTime);
            operatorLogService.save(operatorLogEntity);

            return object;
        }

        return object;
    }





    public static Map getKeyAndValue(Object obj) {
        Map map = new HashMap<>();
        // 得到类对象
        Class userCla = (Class) obj.getClass();
        /* 得到类中的所有属性集合 */
        Field[] fs = userCla.getDeclaredFields();
        for (int i = 0; i < fs.length; i++) {
            Field f = fs[i];
            f.setAccessible(true); // 设置些属性是可以访问的
            Object val = new Object();
            try {
                val = f.get(obj);
                // 得到此属性的值
                map.put(f.getName(), val);// 设置键值
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        return map;
    }




}

3.在要记录日志的接口上添加@OperatorLog注解

基于注解的AOP日志记录_第1张图片

你可能感兴趣的:(Spring,aop,基于注解的AOP日志记录)