自定义注解实现日志记录

前言

平时开发中,我们经常需要通过日志或者数据库来记录系统中一些重要的操作,如删除、修改和新增等。但每次在这些方法里手动打印日志或者记录到数据库有点不现实,像这种统一的操作可以使用spring中的AOP来实现,但是使用AOP切到包或者某种类的话,那只能记录这个包或类下面的日志,不易扩展,这时候就需要自定义注解,然后使用AOP切到这个注解上就行了。

自定义注解

自定义注解首先要知道元注解,也就是注解的注解,是jdk内置的。元注解有四种:

  1. @Retention 注解保留策略

    • @Retention(RetentionPolicy.SOURCE) 仅存在于源码中

    • @Retention(RetentionPolicy.CLASS) 存在于class字节码中,但运行时无法获取

    • @Retention(RetentionPolicy.RUNTIME) 存在于class字节码中,运行时可以通过反射获取

  2. @Target 注解的作用范围

    • @Target(ElementType.TYPE) 接口、类等

    • @Target(ElementType.FIELD) 字段

    • @Target(ElementType.METHOD) 方法

    • @Target(ElementType.PARAMETER) 方法参数

    • @Target(ElementType.CONSTRUCTOR) 构造函数

    • @Target(ElementType.LOCAL_VARIABLE) 局部变量

    • @Target(ElementType.ANNOTATION_TYPE) 注解

    • @Target(ElementType.PACKAGE) 包

  3. @Document 注解包含在javadoc中

  4. @Inherited 注解可以被继承

下面我们来自定义一个注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
    String value() default "";
}

使用@interface来定义一个注解,保留策略使用RetentionPolicy.RUNTIME,因为我们后面使用AOP需要获取它,作用范围使用@Target(ElementType.METHOD),在方法级别上切入日志。

接着要写一个切面来切入这个自定义注解,里面添加逻辑。

  1. 引入jar包
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 编写切面类
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Around("@annotation(myLog)")
    public Object around(ProceedingJoinPoint point, MyLog myLog) throws Throwable{
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        String value = myLog.value();
        log.info("类名:{},方法名:{},注解值:{}",className,methodName,value);
        log.info("方法之前执行");
        long startTime = System.currentTimeMillis();
        Object proceed = point.proceed();
        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        log.info("方法之后执行");
        log.info("方法耗时:{}", time);
        return proceed;
    }

}

首先通过@Around("@annotation(myLog)")切入myLog注解,@Around表示环绕切入,也可以使用@Before和@After

  • @Before 在方法之前执行
  • @After 在方法之后执行
  • @Around 在方法前后都执行

这个方法会传入两个参数ProceedingJoinPoint切点和MyLog注解,通过ProceedingJoinPoint可以获取方法的信息,比如方法名,方法所在的类名等;通过MyLog可以获取注解信息,比如注解传进来的值。

重点:point.proceed(); 这行代码表示执行原始方法,在这行代码之前的代码会在方法之前执行,之后的会在方法执行完以后再执行。返回的Object proceed 是此方法的返回值,上面简单计算了此方法的总耗时。

测试

@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    @MyLog("test-hello")
    @GetMapping("/hello")
    public void hello() {
        log.info("真实的方法执行");
    }
}

输出:

类名:com.example.demo.aspect.DemoController,方法名:hello,注解值:test-hello
方法之前执行
真实的方法执行
方法之后执行
方法耗时:2

这样只要在一些重要操作的类上加上自定义注解@MyLog(“value”)就可以实现自动记录日志了。


扫一扫,关注我

猜你喜欢

转载自blog.csdn.net/weixin_43072970/article/details/106848742