Bootstrap

自定义注解

在SpringBoot相关开发中,我们常常用到各种注解。如@Aotuwired,@Data等等,极大的提高了开发效率,降低代码耦合度。

往往官方只提供常用注解,而自己稀奇古怪的想法就需要自定义开发。

注解分类

@Target(ElementType.)

通过Java盒子类型以及常用注解可以推断出几种注解 :

  • 类注解        如:@Data
  • 方法注解、 如:@GetMapping
  • 属性注解、 如:@Value
  • 形参注解、 如:@RequestBody
@类注解
public class 注解分类{
    
    @属性注解
    public String 属性;


    @方法注解
    public void 方法(){
    }

    
    public void 方法(@参数注解 String 参数)
}

参考Demo 

@Target(ElementType.METHOD)
public @interface InterDemo {
    String value() default "";
}

源码

而根据源码中的枚举类型可以看到,Java代码类型均有对应的类型匹配。

//---------------------------------------源码---------------------------------
public enum ElementType {
    /** Class, interface (including annotation interface), enum, or record
     * declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation interface declaration (Formerly known as an annotation type.) */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE,

    /**
     * Module declaration.
     *
     * @since 9
     */
    MODULE,

    /**
     * Record component
     *
     * @jls 8.10.3 Record Members
     * @jls 9.7.4 Where Annotations May Appear
     *
     * @since 16
     */
    RECORD_COMPONENT;
}

注解存在多久

为什么这么问?

每次项目启动都会出现一个文件夹 out/target ,里面的代码为class文件,在计算机中只有0|1。那么注解将被解析成什么,注解存在到什么时候?

生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。

public enum RetentionPolicy {

    //注解将被编译器忽略掉
    SOURCE,


    //注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留
    CLASS,


    //注解将被编译器记录在class文件中,并且运行时会被虚拟机保留,能通过反射被读取
    RUNTIME
}

一般常用 : RUNTIME。 

注解怎么用,如何生效

我要给注解添加功能,功能在哪添加呢。注解放到类上,是不是包裹一个类呢?放到方法上呢?既然包裹住,那就有执行前和执行后,为什么不用切面当作注解的功能区

基本配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Aspect
@Component
public class AspectDemo {
}

定义切入点

@Pointcut("@annotation(文件路径)")   路径参考 :life.xuxie.www.inter.InterDemo

@Aspect
@Component
public class AspectDemo {
    //定义切入点
    @Pointcut("@annotation(life.xuxie.www.inter.InterDemo)")
    public void interDemoPointcut() {}
}

使用如 @Before 和 @After 注解来定义切面前置和后置通知

@Slf4j
@Aspect
@Component
public class AspectDemo {

    @Pointcut("@annotation(life.putl.wx.inter.InterDemo)")
    public void interDemoPointcut() {}

    @Before("interDemoPointcut()")
    public void before() {
            System.out.println("Before");
    }

    @After("interDemoPointcut()")
    public void after() {
            System.out.println("After");
    }
}

注解参数获取

通过上面的代码可以发现注解生效一部分了,因为我们定义的值根本没用到....

获取参数方法就是上面说的通过反射,我们需要在方法中添加形参

获取方法签名:通过 joinPoint.getSignature() 获取方法签名,并将其转换为 MethodSignature。
获取方法对象:通过 MethodSignature 获取方法对象 Method。
获取注解:通过 method.getAnnotation(InterDemo.class) 获取方法上的 @InterDemo 注解。
提取注解值:从注解对象中提取 value 值,并打印出来。 

@Slf4j
@Aspect
@Component
public class AspectDemo {

    @Pointcut("@annotation(life.putl.wx.inter.InterDemo)")
    public void interDemoPointcut() {}

    @Before("interDemoPointcut()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        InterDemo interDemo = method.getAnnotation(InterDemo.class);
        if (interDemo != null) {
            String value = interDemo.value();
            System.out.println("Before: " + value);
        }
    }

    @After("interDemoPointcut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        InterDemo interDemo = method.getAnnotation(InterDemo.class);
        if (interDemo != null) {
            String value = interDemo.value();
            System.out.println("After: " + value);
        }
    }

    @Around("interDemoPointcut()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 执行目标方法
        Object result = joinPoint.proceed();

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        log.info("{} executed in {} ms", joinPoint.getSignature(), executionTime);

        return result;
    }
}

;