自定义注解结合AOP环绕通知记录操作日志

开发环境: springboot + maven + ssm

开发工具: idea

        写这篇文章,一方面是方便广大java开发同胞,另一方面也是存储代码,哈哈。


声明自定义注解(jdk1.5之后)

    

import com.quantum.ferrari.enums.simple.OperationLogLevelEnum;
import com.quantum.ferrari.enums.simple.OperationModuleEnum;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.FIELD, ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法
@Documented//说明该注解将被包含在javadoc中
public @interface OpeAnnotation {

    /**
     * 模块名称
     *
     * @return
     */
    OperationModuleEnum modelName() default OperationModuleEnum.Default;

    /**
     * 类型名称
     *
     * @return
     */
    String typeName() default "";

    OperationLogLevelEnum logLevel() default OperationLogLevelEnum.INFO;

}

声明切面,使用环绕通知切到有自定义注解的方法

import com.alibaba.fastjson.JSONObject;
import com.quantum.ferrari.controller.BaseController;
import com.quantum.ferrari.enums.simple.OperationLogLevelEnum;
import com.quantum.ferrari.enums.simple.OperationModuleEnum;
import com.quantum.ferrari.model.manager.UserModel;
import com.quantum.ferrari.model.simple.OperationLogModel;
import com.quantum.ferrari.service.RedisService;
import com.quantum.ferrari.service.simple.OperationLogService;
import com.quantum.ferrari.vo.UserLoginSession;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 实现用户crud操作的日志记录
 *
 * @author liujt
 */
@Aspect
@Component
public class OperationAspect extends BaseController {

    @Resource
    private RedisService redisService;

    @Autowired
    private OperationLogService operationLogService;

    @Around("@annotation(opeAnnotation)")
    public Object doAfterReturning(ProceedingJoinPoint pjp, OpeAnnotation opeAnnotation) {
        Object proceed = null;
        try {
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //模块
        OperationModuleEnum operationModuleEnum = opeAnnotation.modelName();
        //操作类型
        String typeStr = opeAnnotation.typeName();
        //请求参数
        JSONObject requestParamsJSON = getRequestParams();
        //响应参数
        JSONObject responseParamsJSON = getResponseParams(proceed);
        //结果集
        JSONObject resultJSON = getResult(proceed);
        //获取用户id和用户名称   id 0 名称 1
        String[] split = getUserIdAndName().split(",");
        OperationLogLevelEnum operationLogLevelEnum = opeAnnotation.logLevel();
        //饿汉式单例获取操作日志model
        OperationLogModel ope = OperationLogModelSingleton.getInstance();
        ope.setOpeModule(operationModuleEnum);
        ope.setOpeType(typeStr);
        ope.setOpeUserid(split[0]);
        ope.setOpeUsername(split[1]);
        ope.setOpeReqParam(requestParamsJSON.toJSONString());
        ope.setOpeRespParam(responseParamsJSON.toJSONString());
        ope.setOpeResult(resultJSON.toJSONString());
        ope.setOperationLogLevelEnum(operationLogLevelEnum);
        operationLogService.saveOperationLog(ope);
        return proceed;
    }

    public String getUserIdAndName() {
        UserLoginSession userLoginSession = this.getUserLoginSession();
        UserModel userModel = userLoginSession.getUserModel();
        //用户id和用户名称拼接起来
        String userIdAndName = userModel.getId() + "," + userModel.getUserName();
        return userIdAndName;
    }

    private JSONObject getResponseParams(Object proceed) {
        JSONObject jsonObject = new JSONObject();
        Field[] fields = proceed.getClass().getDeclaredFields();
        if (null != fields) {
            for (int i = 0, len = fields.length; i < len; i++) {
                // 对于每个属性,获取属性名
                String varName = fields[i].getName();
                try {
                    // 获取原来的访问控制权限
                    boolean accessFlag = fields[i].isAccessible();
                    // 修改访问控制权限
                    fields[i].setAccessible(true);
                    // 获取在对象f中属性fields[i]对应的对象中的变量
                    Object o;
                    try {
                        o = fields[i].get(proceed);
                        jsonObject.put(varName, o);
                   /* String jsonObjecttoString = jsonObject.toString();
                    JSONObject.parseObject(jsonObjecttoString);*/
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    // 恢复访问控制权限
                    fields[i].setAccessible(accessFlag);
                } catch (IllegalArgumentException ex) {
                    ex.printStackTrace();
                }
            }
        } else {
            jsonObject.put("key", "没有请求参数");
        }
        return jsonObject;
    }

    private JSONObject getResult(Object proceed) {


        JSONObject jsonObject = new JSONObject();
        Field[] fields = proceed.getClass().getDeclaredFields();
        if (null != fields) {
            for (int i = 0, len = fields.length; i < len; i++) {
                // 对于每个属性,获取属性名
                String varName = fields[i].getName();
                try {
                    // 获取原来的访问控制权限
                    boolean accessFlag = fields[i].isAccessible();
                    // 修改访问控制权限
                    fields[i].setAccessible(true);
                    // 获取在对象f中属性fields[i]对应的对象中的变量
                    Object o;
                    try {
                        o = fields[i].get(proceed);
                        if (i == fields.length - 1) {
                            jsonObject.put(varName, o);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    // 恢复访问控制权限
                    fields[i].setAccessible(accessFlag);
                } catch (IllegalArgumentException ex) {
                    ex.printStackTrace();
                }
            }
        } else {
            jsonObject.put("key", "没有结果集");
        }
        return jsonObject;
    }

    public JSONObject getRequestParams() {
        JSONObject jsonObject = new JSONObject();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Enumeration enu = request.getParameterNames();
        if (null != enu) {
            while (enu.hasMoreElements()) {
                String paraName = (String) enu.nextElement();
                jsonObject.put(paraName, request.getParameter(paraName));
            }
        } else {
            jsonObject.put("key", "没有传入参数");
        }
        return jsonObject;
    }

}
 
  

大致就是这样,需要注意的是,在记录操作日志的时候,要使用多线程。使用多线程原因有两点:1.提高运行效率 2.异步添加操作日志。第二点比较重要,也就是说无论我在记录操作日志的时候是否报错,都不能干扰项目的操作。

IDEA上使用,引入:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>



你可能感兴趣的:(自定义注解结合AOP环绕通知记录操作日志)