Springboot采用AOP实现记录日志功能

一、定义一个Annotation类

Annotation的中文意思就是注解,注解的具体意思可以查询菜鸟教程。在这个注解类上需要加上其他三个注解,分别是 @Target@Retention@Documented,可以随便点击Springboot中的其他注解(如@Controller,@Service等)可以发现,他们三个都是一直出现的,所有注解都需要加。

import java.lang.annotation.*;

/**
 * TYPE代表可以放在类上边,MTTHOD代表可以放在方法上
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    String module() default "";

    String operator() default "";

}
复制代码

二、定义切面类Aspect

  1. 声明了注解之后,就需要定义一个切面类,来实现我们所需要的所有操作。在pt()函数上添加 @Pointcut注解表示切入点,内容为自己刚刚声明的@Annotation的包地址。
  2. 定义log函数,添加 @Around 注解,用于环绕pt()函数。
import com.alibaba.fastjson.JSON;
import com.jyuxuan.blog.utils.HttpContextUtils;
import com.jyuxuan.blog.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
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.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Component
@Aspect // 切面,定义了通知和切点的关系
@Slf4j
public class LogAspect {

    @Pointcut("@annotation(com.***.blog.common.aop.LogAnnotation)")
    public void pt() {
    }

    /**
     * 环绕通知
     */
    @Around("pt()")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = joinPoint.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        // 保存日志
        recordLog(joinPoint, time);
        return result;
    }

    private void recordLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        log.info("=====================log start================================");
        log.info("module:{}", logAnnotation.module());
        log.info("operation:{}", logAnnotation.operator());

        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.info("request method:{}", className + "." + methodName + "()");

        // 请求的参数
        Object[] args = joinPoint.getArgs();
        String params = JSON.toJSONString(args[0]);
        log.info("params:{}", params);

        //获取 request 设置IP地址
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        log.info("ip:{}", IPUtils.getIpAddr(request));

        log.info("excute time : {} ms", time);
        log.info("=====================log end================================");
    }

}
复制代码

三、在Controller方法中使用注解

在自己的随便的一个方法上,添加注解,即可在工作台上查看日志信息。

@PostMapping
@LogAnnotation(module = "名称", operator = "操作名称")
public Result test(@RequestBody Work work) {
    return Result.success(null);
}
复制代码

四、补充内容

1、@Target

随便在一个注解上点击进入,即可看到@Target注解,然后点进去@Target注解,发现以下内容。再次点击ElementType

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
复制代码

在这里可以看到@Target可以注解的全部内容信息,全部写在注释中了。

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 // 能标注任何类型
}
复制代码

2、@Retention

指明修饰的注解的生存周期,即会保留到哪个阶段,点击进入。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
复制代码

再次点击RetentionPolicy。

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.
     */
    CLASS, // 编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,(默认值)

    /**
     * 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.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME // 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用
}
复制代码

3、@Documented

表明这个注释是由 javadoc记录的,表明该注解下的类会出现在java doc html文档中。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
复制代码

猜你喜欢

转载自juejin.im/post/7039699362422734879