• 😜           :是江迪呀
  • ✒️本文关键词Java高级应用自定义注解注解
  • ☀️每日   一言追求潮流,其本身一点都不潮流!

java自定义FIELD注解不生效_java

一、前言

使用 Java 注解(Annotations)可以在代码中添加元数据,提供了一种将元数据与代码关联的方式。注解可以在类、方法、字段等各种元素上使用,以提供附加的信息,这些信息可以被编译器、工具或者运行时使用。注解编程也是Java知识体系中很重要的一环。

二、什么是注解

在计算机编程中,注解(Annotation)是一种为代码添加元数据(metadata)的方式,用于提供关于代码的附加信息。注解可以用于类、方法、字段、参数等程序元素,以便在编译时、运行时或者使用特定工具时进行处理。注解是一种用于描述代码的元数据,它本身不会影响代码的逻辑运行,但可以为代码提供一些附加的信息。这些信息可以用于编译器的优化、代码分析、文档生成、测试等各种用途。

2.1 注解作用:

注解可以用来处理一些繁琐而具有共性的东西,比如日志添加、鉴权,可以有效的减少代码量、配置项从而提高代码的可读性、一致性。

2.2 注解分类

注解一般分为预定义注解自定义注解

(1)预定义注解:

Java中有很多预定义注解,比如:

  • @Override:标记方法覆盖父类方法。
  • @Deprecated:标记方法或类已过时,不推荐使用。
  • @SuppressWarnings:抑制编译器警告。
  • @FunctionalInterface:标记接口是一个函数式接口。
    等等……这些注解可以直接用于相应的情况。

(2)自定义注解:

Java 还支持自定义注解。自定义注解使用 @interface 关键字定义,并可以在注解内部定义元素。自定义注解可以带有默认值,也可以指定保留策略(源代码、类文件、运行时等)。

三、自定义注解

3.1 实现一个注解

如果你想要自定义一个注解是很简单的,如下代码所示:

public @interface MyAnnotation {
	// 定义一个属性名为 "value"
    String value() default ""; 
    // 定义一个属性名为 "count",并设置默认值
    int count() default 1; 
}

如果你想使用:

@MyAnnotation(value = "Hello", count = 3)
public class MyClass {
...
}

非常简单。

3.2 注解属性介绍

我们可以在自定义注解类上面加上元注解,那么有哪些元注解可以使用,他们的作用是什么?如下所示:

(1)@Retention:这个元注解指定了自定义注解的保留策略,即注解在什么级别保留。它有以下取值:

  • RetentionPolicy.SOURCE:注解仅保留在源代码中,编译后不包含在字节码中。
  • RetentionPolicy.CLASS:注解保留在字节码中,但不会在运行时被反射获取(默认值)。
  • RetentionPolicy.RUNTIME:注解保留在字节码中,并可以在运行时通过反射获取(实际场景中使用最多)。

(2)@Target: 这个元注解指定了自定义注解可以应用在哪些元素上,如果自定义的注解只能作用于方法上,那么就不能作用于类上。它有以下取值:

  • ElementType.TYPE:作用于类、接口、枚举等类型。 ElementType.FIELD:字段。
  • ElementType.METHOD:作用于方法。 ElementType.PARAMETER:方法参数。
  • ElementType.FIELD:作用于属性。
  • ElementType.PACKAGE:用于包声明。
  • ElementType.LOCAL_VARIABLE:用于局部变量。
  • ElementType.CONSTRUCTOR:作用于构造函数。 ElementType.LOCAL_VARIABLE:局部变量。
  • ElementType.ANNOTATION_TYP:作用于注解类型。 ElementType.PACKAGE:包。
  • ElementType.TYPE_PARAMETER:作用于类型参数(Java 8+)。
  • ElementType.TYPE_USE:作用于类型使用(Java 8+)。

(3)@Documented: 这个元注解用于指定自定义注解是否应该包含在Java文档中。如果一个注解被标记为 @Documented,则它会出现在生成的API文档中。
(4)@Inherited: 这个元注解用于指定自定义注解是否可以被继承。如果一个注解被标记为 @Inherited,则子类会继承父类上的相同注解。

四、使用场景

4.1 对方法添加文档

我们有一个简单的用户类,我们想要为其中的某些方法添加一些说明文档。我们可以使用自定义注解来实现这个目标。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodDocumentation {
    String value() default "";
}
import cn.trueland.anno.MethodDocumentation;
import lombok.Data;

@Data
public class User {
    private String userId;

    @MethodDocumentation("这个方法返回的是用户的姓名!")
    public String getName() {
        return "是江迪呀";
    }
}

在其他地方使用反射来获取注解的信息:

import cn.trueland.anno.MethodDocumentation;
import cn.trueland.model.User;

import java.lang.reflect.Method;

public class TestRunMain {
    public static void main(String[] args) {
        User user = new User();
        Method[] methods = user.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MethodDocumentation.class)) {
                MethodDocumentation annotation = method.getAnnotation(MethodDocumentation.class);
                System.out.println("Method: " + method.getName());
                System.out.println("Documentation: " + annotation.value());
                System.out.println();
            }
        }
    }

}

结果:

Method: getName
Documentation: 这个方法返回的是用户的姓名!

4.2 日志记录

如果单独使用自定义注解,能够实现的东西很少,但是如果加上AOP那么应用的场景就很多了。
下面是自定义注解 + AOP实现的一个记录日志的代码示例:
(1)日志实体类:

import lombok.Data;

@Data
public class Log {
    /**
     * 操作类型
     */
    private String operationType;
    /**
     * 操作内容
     */
    private String operationContent;
    /**
     * 操作人id
     */
    private Long userId;
    /**
     * 操作人名称
     */
    private String userName;
}

(2)自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAnnotation {
    /**
     * 操作类型
     */
    String OperationType() default "";
    /**
     * 操作内容
     */
    String OperationContent() default "";
    /**
     * 操作人id
     */
    long UserId() default 0;
    /**
     * 操作人名称
     */
    String UserName() default "";
}

(3)将注解属性转为注解对象工具类:

public class AnnotationUtil {
    public static <T extends Annotation> T getAnnotationMessageClass(JoinPoint joinPoint, Class<T> tClass) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (null == method) {
            return null;
        }
        return method.getAnnotation(tClass);
    }
}

(4)AOP切面:

@Aspect
@Component
@Slf4j
public class LogAop {
    //切面的路径就是你自定义注解的路径
    @Pointcut("@annotation(cn.trueland.anno.LogAnnotation)")
    public void pointCut(){}

    //使用后置通知
    @After("pointCut()")
    public void beforeAop(JoinPoint joinPoint){
        //获取日志对象
        LogAnnotation logAnnotation = AnnotationUtil.getAnnotationMessageClass(joinPoint, LogAnnotation.class);
        //处理日志入库逻辑
        System.out.println("操作类型:" + logAnnotation.OperationType());
        System.out.println("操作内容:" +logAnnotation.OperationContent());
        System.out.println("操作人id:" +logAnnotation.UserId());
        System.out.println("操作用户名:" +logAnnotation.UserName());
    }
}

(5)具体的方法

@LogAnnotation(OperationType = "添加",OperationContent = "添加用户信息",UserId = 01,UserName="是江迪呀")
    public void add(){
        //业务逻辑
    }

(6)运行结果:

操作类型:添加
操作内容:添加用户信息
操作人id:1
操作用户名:是江迪呀