java AOP 切面和注解记录接口操作日志

  • Post author:
  • Post category:java




java 切面实现记录操作日志



一、自定义注解

自定义注解

本次操作通过自定义注解来实现,声明自定义注解

常用

的注解

自定义注解的成员可以是八种基本类型(byte、short、char、int、long、float、double、boolean)和String、Enum、Class、annotations、数组等数据类型

声明方式:

@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface 类名 {
     //多个自定义成员
     String value() default "";
}

1.@Retention (在自定义的注解上面添加此注解,表示自定义注解可以保留在那个阶段,一共有三个阶段)

源码:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * 注释将被编译器丢弃
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * 注释将由编译器记录在类文件中,但不需要在运行时由 VM(java 虚拟机) 保留。这是默认行为
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *注释将由编译器记录在类文件中,并在运行时由 VM(java 虚拟机) 保留,因此可以反射性地读取它们。<-常用
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

1.@Retention(RetentionPolicy.RUNTIME) 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在.

2.@Retention(RetentionPolicy.CLASS) 注解被保留到编译后的class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期.

3.@Retention(RetentionPolicy.SOURCE) 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃,被编译器忽略.

级别:RUNTIME>CLASS>SOURCE

2.@Target (在自定义注解上面添加该注解,表示自定义注解能运用在哪些位置,该注解的内部成员由枚举实现,一共有10种类型)

源码:

public enum ElementType {
    /**
     * Class, interface (including annotation type), or enum declaration
     * 作用于类上
     */
    TYPE,
    /**
     * Field declaration (includes enum constants)
     * 作用于字段
     */
    FIELD,
    /**
     * Method declaration
     * 作用于方法上
     */
    METHOD,
    /**
     * Formal parameter declaration
     * 作用于方法的参数
     */
    PARAMETER,
    /**
     * Constructor declaration
     * 作用于构造函数
     * */
    CONSTRUCTOR,
    /**
     *  Local variable declaration
     *  用于局部变量
     */
    LOCAL_VARIABLE,
    /**
     *  Annotation type declaration
     *  用于注释类型
     */
    ANNOTATION_TYPE,
    /**
     * Package declaration
     * 用于包声明
     */
    PACKAGE,
    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,
    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

1.ElementType.TYPE 可以应用于类的任何元素(类、接口、注解、enum)。

2.ElementType.FIELD 可以应用于字段或属性(成员变量、对象、属性、枚举的常量)。

3.ElementType.METHOD 可以应用于方法级注释。

4.ElementType.PARAMETER 可以应用于方法的参数。

5.ElementType.CONSTRUCTOR 可以应用于构造函数。

6.ElementType.LOCAL_VARIABLE 可以应用于局部变量。

7.ElementType.ANNOTATION_TYPE 可以应用于注释类型。

8.ElementType.PACKAGE 可以应用于包声明。

9.ElementType.TYPE_PARAMETER 类型参数,表示这个注解可以用在 Type的声明式前。

10.ElementType.TYPE_USE 类型的注解,表示这个注解可以用在所有使用Type的地方(如:泛型,类型转换等)。




二、AOP切面实现日志操作记录

以上纯属注解的解释,开始正题



1.自定义日志注解
package com.lb.annonation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @Author LuoBo
 * @Date 2022/1/26
 * @description  测试切面日志(自定义注解)
 */
@Target({ElementType.METHOD}) //应用于方法上面
@Retention(RetentionPolicy.RUNTIME)//表示在运行时注解任可用
public @interface OperationLogAnno {
    /**
     * 操作位置
     */
    String operatePage() default "";
    /**
     * 操作类型
     */
    String operateType() default "";
    /**
     * 业务域,各自业务自己定义
     */
    String bizType() default "";
    /**
     * 操作类型(枚举类型)
     */
    OperationType operationType() default OperationType.OTHER;
}

操作类型(枚举类型)

package com.lb.annonation;

/**
 * @Author LuoBo
 * @Date 2022/1/26
 */
public enum OperationType {
    /**
     * 创建
     */
    CREATE,
    /**
     * 批量创建
     */
    BATCH_CREATE,
    /**
     * 更新
     */
    UPDATE,
    /**
     * 批量更新
     */
    BATCH_UPDATE,
    /**
     * 删除
     */
    DELETE,
    /**
     * 批量删除
     */
    BATCH_DELETE,
    /**
     * 导入
     */
    IMPORT,
    /**
     * 导出
     */
    EXPORT,
    /**
     * 其他类型
     */
    OTHER,
    ;
}


2.切面类

切面实现日志记录(此处使用前置通知的方式记录接口调用的情况)

package com.lb.aspect;

import cn.hutool.core.date.DateUtil;
import com.lb.annonation.OperationLogAnno;
import com.lb.mapper.AspectLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

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

@Slf4j
@Aspect
@Configuration
public class OperationAspect {

    @Autowired
    private AspectLog aspectLog;
    //切点
    @Pointcut("@annotation(com.lb.annonation.OperationLogAnno)")
    private void cutMethod() {

    }
    //
    @Before("cutMethod()")
    public void before(JoinPoint joinPoint) throws Throwable {
        // 获取方法名
        //String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型(重载)获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);

        // 获取方法上声明的注解
        OperationLogAnno anno = objMethod.getDeclaredAnnotation(OperationLogAnno.class);
        //记录日志时间
        String formatDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"); 
        //调用接口保存日志
        aspectLog.addLog(anno.operatePage(),anno.operateType(),anno.bizType(),anno.operationType(), formatDate);
    }
}

mapper层AspectLog

/**
 * @Author LuoBo
 * @Date 2022/1/26
 * @description  测试切面日志
 */
@Repository
public interface AspectLog {
    @Insert("insert into log (operate,type,methodRoot,realType,operateTime) values (#{operate},#{type},#{methodRoot},#{realType},#{time})")
    void addLog(String operate, String type, String methodRoot, OperationType realType,String time);
}

接口测试:

package com.lb.controller;

import cn.hutool.core.date.DateUtil;
import com.lb.annonation.OperationLogAnno;
import com.lb.annonation.OperationType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @Author LuoBo
 * @Date 2022/1/26
 * @description
 */
@RestController
@RequestMapping("/aspect")
public class TestAspectController {

    @PostMapping("/update")
    @OperationLogAnno(operatePage = "测试修改用户数据",operateType = "更新",bizType = "/aspect/update",operationType = OperationType.UPDATE)
    public void testUpdate(){
        String format = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        System.out.println("测试操作日志,方法调用成功!"+System.currentTimeMillis()+"  "+format);
    }

}

测试结果:

在这里插入图片描述

数据库数据更新成功:

在这里插入图片描述



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