java自定义注解实现日志管理

  1. 首先创建一个springboot项目

  2. 然后添加aop的依赖和lombok依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>
  1. 编写LogInfo

    package com.cxp.utils;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author Angelou
     * @date 2021/10/28
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LogInfo {
        String value() default "";
    }
    
    
  2. 编写Log的切面

    package com.cxp.utils;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    /**
     * log的切面
     *
     * @author Angelou
     * @date 2021/10/28
     */
    @Aspect
    @Component
    @Slf4j
    public class LogAspect {
        /**
         * 配置切入点,该方法无方法体,主要为了方便同类中其他方法使用此处配置的切入点
         */
        @Pointcut("@annotation(com.cxp.utils.LogInfo)")
        public void pointcut() {
    
        }
    
        /**
         * aop前置
         *
         * @param joinPoint
         */
        @Before("pointcut()")
        public void before(JoinPoint joinPoint) {
            log.info("before通知:" + joinPoint);
        }
    
        /**
         * aop后置
         *
         * @param joinPoint
         */
        @After("pointcut()")
        public void after(JoinPoint joinPoint) {
            log.info("after通知:" + joinPoint);
    
        }
    
        /**
         *
         */
        @Around("pointcut()")
        public Object around(JoinPoint joinPoint) throws Throwable {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            LogInfo annotation = method.getAnnotation(LogInfo.class);
            String value = annotation.value();
            //拿注解括号的内容
            log.info(value);
            log.info("after通知" + joinPoint);
            log.info("打印目标方法名" + joinPoint.getSignature().getName());
            log.info("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
            log.info("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
            log.info("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
    //        log.info("获取目标传入目标方法的参数"+joinPoint.getArgs());
            return ((ProceedingJoinPoint) joinPoint).proceed();
        }
    
        /**
         * 方法return后
         *
         * @param joinPoint
         */
        @AfterReturning("pointcut()")
        public void afterReturn(JoinPoint joinPoint) {
    
            log.info("afterReturn 通知" + joinPoint);
        }
    
        /**
         * 方法抛出异常是调用
         *
         * @param joinPoint
         * @param ex
         */
        @AfterThrowing(pointcut = "pointcut()", throwing = "ex")
        public void afterThrow(JoinPoint joinPoint, Exception ex) {
    
            log.info("afterThrow 通知" + joinPoint + "\t" + ex.getMessage());
    
        }
    }
    
  3. 编写LogController

    package com.cxp.controller;
    
    import com.cxp.utils.LogInfo;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author Angelou
     * @date 2021/10/28
     */
    @RestController
    @RequestMapping("/log")
    public class LogController {
        @LogInfo("进入了logDemo1")
        @RequestMapping("/logDemo1")
        public void logDemo1(){
            System.out.println("logDemo1方法体内部");
        }
    }
    
  4. 测试
    输入http://localhost:8080/log/logDemo1

  5. 控制台的输出

    2021-10-28 18:06:55.347  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : 进入了logDemo1
    2021-10-28 18:06:55.347  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : after通知execution(void com.cxp.controller.LogController.logDemo1())
    2021-10-28 18:06:55.347  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : 打印目标方法名logDemo1
    2021-10-28 18:06:55.347  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : 目标方法所属类的简单类名:LogController
    2021-10-28 18:15:53.930  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : 目标方法所属类的类名:com.cxp.controller.LogController
    2021-10-28 18:15:53.930  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : 目标方法声明类型:public
    2021-10-28 18:15:53.931  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : before通知:execution(void com.cxp.controller.LogController.logDemo1())
    logDemo1方法体内部
    2021-10-28 18:15:53.935  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : afterReturn 通知execution(void com.cxp.controller.LogController.logDemo1())
    2021-10-28 18:15:53.935  INFO 4308 --- [nio-8080-exec-1] com.cxp.utils.LogAspect                  : after通知:execution(void com.cxp.controller.LogController.logDemo1())
    

    附录

    AOP术语

    AOP 领域中的特性术语:

    • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
    • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
    • 切点(PointCut): 可以插入增强处理的连接点。
    • 切面(Aspect): 切面是通知和切点的结合。
    • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
    • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

    @interface注解详解

    @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)——包

    @Retention:注解的保留位置

    RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。
    RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。
    RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。

    @Document:说明该注解将被包含在javadoc中

    @Inherited:说明子类可以继承父类中的该注解

在这里插入图片描述

@Aspect注解详解

Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
Aspect:切面,即Pointcut和Advice。
Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

@Pointcut注解详解

@Pointcut 注解指定一个切点,定义需要拦截的东西,这里介绍两个常用的表达式:一种是用execution() ,另一个是使用annotation()

execution()表达式:

execution(* com.cxp.controller..*.*(..)))表达式为例:

第一个 * 号的位置:表示返回值类型,* 表示所有类型。
包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,在本例中指 com.mutest.controller包、子包下所有类的方法。
第二个 * 号的位置:表示类名,* 表示所有类。
(…):这个星号表示方法名, 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

annotation()表达式:

annotation() 方式是针对某个注解来定义切点,比如我们对具有 @PostMapping 注解的方法做切面,可以如下定义切面:

@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void annotationPointcut() {}

然后使用该切面的话,就会切入注解是 @PostMapping 的所有方法。这种方式很适合处理 @GetMapping、@PostMapping、@DeleteMapping不同注解有各种特定处理逻辑的场景。

还有就是如上面案例所示,针对自定义注解来定义切面。

@Pointcut("@annotation(com.example.demo.PermissionsAnnotation)")
private void permissionCheck() {}

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