一首先需要定义一个注解的interface,也就是我们自定义注解的注解名,同时也要定义里面的内部参数

package com.common.log.vo;
import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {


    /**
     * 模块
     *
     */
    String moduleName() default "";


   
    String sheetType() default "";


   
    String operationDesc() default "";


  
    String sourceNoField() default "";


    /**
     * 单据编码字段名称
     *
     */
    String businessKeyField();
}

在SpringBoot中,有四个元注解,它们被称为注解的注解,我们在自定义注解的时候,就会用到它们,下面对他们分别做个简单的介绍:

@Target:这个注解就是规定了我们自定义的注解所使用的的范围,里面有个属性叫ElementType,关于这个属性有着很多值:

@Target(ElementType.TYPE) 接口、类
@Target(ElementType.FIELD) 属性
@Target(ElementType.METHOD) 方法
@Target(ElementType.PARAMETER) 方法参数 parameter
@Target(ElementType.CONSTRUCTOR) 构造函数constructor
@Target(ElementType.LOCAL_VARIABLE) 局部变量
@Target(ElementType.ANNOTATION_TYPE) 注解
@Target(ElementType.PACKAGE) 包

@Retention:定义注解的保留策略 有以下三种

@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不含
@Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.
@Inherited:这个注解是一个标记注解,表明被标注的类型是可以被继承的。

二 写切面逻辑

在逻辑里实现对方法输出结果的捕获与处理,持久化。我们知道一个方法的输出结果无非是两种,一个是方法执行成功,输出正确的结果;一个是方法执行失败,中途报错。对于这两种不同的执行结果,分别都需要用不同的逻辑来进行处理。

package com.cpit.ycyt.psc.common.logaspect;

import com.cpit.ycyt.common.core.util.JsonUtil;
import com.cpit.ycyt.common.security.util.SecurityUtil;
import com.cpit.ycyt.psc.common.domain.PscLog;
import com.cpit.ycyt.psc.common.logaspect.vo.OperationLog;
import com.cpit.ycyt.psc.common.mapper.PscLogCommonMapper;
import com.cpit.ycyt.upms.api.domain.UserDetail;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


@Component
@Aspect
@Slf4j
public class OperationLogAspect {
    @Autowired
    SecurityUtil securityUtil;
    @Autowired
    PscLogCommonMapper pscLogCommonMapper;

    @Pointcut(value = "@annotation(com.cpit.ycyt.psc.common.logaspect.vo.OperationLog)")
    public void beanAnnotatedWithController() {
    }

    @Pointcut(value = "@annotation(operationLog)", argNames = "operationLog")
    private void operationLogMethod(OperationLog operationLog) {
    }

    /**
     * 管理员注解拦截操作日志
     *
     * @param joinPoint    切点
     * @param operationLog 自定义注解
     * @return Object
     * @throws Throwable
     */
       @Around(value = "beanAnnotatedWithController() && @annotation(operationLog)",argNames = "joinPoint,operationLog")
    public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        final Object retVal = joinPoint.proceed();
        long endTime = System.currentTimeMillis();

        String moduleName = operationLog.moduleName();
        String sheetType = operationLog.sheetType();
        String operationDesc = operationLog.operationDesc();
        String businessKeyField = operationLog.businessKeyField();
        String sourceNoField = operationLog.sourceNoField();

 
        Map<String, Object> nameAndArgs = joinArgs(joinPoint);
        // 处理操作人信息
        UserDetail user = getUser();

        String reqMsg = JsonUtil.toJSONString(nameAndArgs);
        PscLog model = new PscLog();
        model.setModuleName(moduleName).setSheetType(sheetType).setRequestTime(startTime).setOperationDesc(operationDesc).setSourceNo(sourceNo).setSheetNo(sheetNo)
                .setReqMsg(reqMsg).setErrorMsg(JsonUtil.toJSONString(retVal)).setOperator(user.getUserCode() + "_" + user.getUserName())
                .setOperatingTime((endTime - startTime) / 1000).setCreateTime(new Date());

        pscLogCommonMapper.insert(model);
        return retVal;
    }

    /**
     * 管理员注解拦截操作异常日志
     *
     * @param joinPoint    切点
     * @param operationLog 自定义注解
     * @return
     * @throws Throwable
     */
    @AfterThrowing(value = "beanAnnotatedWithController() && operationLogMethod(operationLog)", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, OperationLog operationLog, Exception ex) {
        long startTime = System.currentTimeMillis();

        String moduleName = operationLog.moduleName();
        String sheetType = operationLog.sheetType();
        String operationDesc = operationLog.operationDesc();
        String businessKeyField = operationLog.businessKeyField();
        String sourceNoField = operationLog.sourceNoField();

       
        // 处理操作人信息
        UserDetail user = getUser();
        String reqMsg = JsonUtil.toJSONString(nameAndArgs);

        PscLog model = new PscLog();
        model.setModuleName(moduleName).setSheetType(sheetType).setRequestTime(startTime).setOperationDesc(operationDesc).setSourceNo(sourceNo)
                .setSheetNo(sheetNo).setReqMsg(reqMsg).setErrorMsg(ex.toString() + "," + JsonUtil.toJSONString(ex.getStackTrace()).substring(0, 500))
                .setOperator(user.getUserCode() + "_" + user.getUserName()).setOperatingTime(0L).setCreateTime(new Date());

        pscLogCommonMapper.insert(model);
    }

    /**
     * 获取登录用户信息
     *
     * @return
     */
    private UserDetail getUser() {
        try {
            UserDetail user = securityUtil.getUser();
            if (Objects.isNull(user)) {
                user = new UserDetail();
                user.setUserCode("auto");
                user.setUserName("auto");
            }
            return user;
        }catch (NullPointerException e) {
            // 接口调用均不带TOKEN,会报空指针异常,不进行日志记录
            UserDetail user = new UserDetail();
            user.setUserCode("error");
            user.setUserName("error");
            return user;
        } catch (Exception e) {
            log.error("=======日志记录切面-获取用户信息异常。异常信息:{}=======", e.getMessage());
            UserDetail user = new UserDetail();
            user.setUserCode("error");
            user.setUserName("error");
            return user;
        }

    }


    /**
     * 组合方法参数为Map
     *
     * @param joinPoint
     * @return
     */
    public static Map<String, Object> joinArgs(JoinPoint joinPoint) {
        Map<String, Object> map = new HashMap<>();
        Object[] objects = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] names = methodSignature.getParameterNames();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], objects[i]);
        }
        return map;
    }
}

最终在调用接口上面添加注解即可成功使用

@OperationLog(moduleName = "web", sheetType = "", operationDesc = "手动生成", businessKeyField = "sheetNo")
    public HttpResult getUserInfo(@RequestParam("sheetNo") String sheetNo, @RequestParam("sheetType") String sheetType) {```