一篇文章,全面掌握Java自定义注解(Annontation)

简介: 一篇文章,全面掌握Java自定义注解(Annontation)

如果此刻不方便阅读,可收藏或关注微信公众号《程序新视界》回复“注解”获得PDF版本。


注解简介

注解(Annontation),Java5引入的新特性,位于java.lang.annotation包中。提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。是一种说明、配置、描述性的信息,与具体业务无关,也不会影响正常的业务逻辑。但我们可以用反射机制来进行校验、赋值等操作。


常见的注解:@Override,@author,@param,@Deprecated,@SuppressWarnings。


注解的常见用途:

生成文档的注解,如@author,@param。

跟踪代码依赖性,实现替代配置文件功能,如spring mvc的注解。

编译时进行格式检查,如@override。

编译时进行代码生成补全,如lombok插件的@Data。

注解基础知识

注解的定义

注解的定义通过@interface表示,所有的注解会自动继承java.lang.Annotation接口,且不能再继承别的类或是接口。

注解的成员参数只能用public或默认(default) 访问权修饰来进行修饰。

成员参数只能使用八种基本类型(byte、short、char、int、long、float、double、boolean)和String、Enum、Class、annotations等数据类型,及其数组。

获取类方法和字段的注解信息,只能通过Java的反射技术来获取 Annotation 对象。

注解可以没有定义成员,只做标识。

元注解

元注解是专门用来注解其他注解的注解,听起来有些绕口,实际上就是专门为自定义注解提供的注解。java.lang.annotation提供了四种元注解:


@Documented – 注解是否将包含在JavaDoc中

@Retention – 什么时候使用该注解

@Target – 注解用于什么地方

@Inherited – 是否允许子类继承该注解

@Repeatable - 是否可重复注解,jdk1.8引入

注解的生命周期

通过@Retention定义注解的生命周期,格式如下:


@Retention(RetentionPolicy.SOURCE)

1

其中RetentionPolicy的不同策略对应的生命周期如下:


RetentionPolicy.SOURCE : 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中。@Override, @SuppressWarnings都属于这类注解。

RetentionPolicy.CLASS : 默认策略,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到。

RetentionPolicy.RUNTIME : 始终不会丢弃,可以使用反射获得该注解的信息。自定义的注解最常用的使用方式。

注解的作用目标

通过@Target定义注解作用的目标,比如作用于类、属性、或方法等,默认可用于任何地方。格式如下:


@Target(ElementType.TYPE)

1

对应ElementType参数值适用范围如下:


ElementType.TYPE: 类、接口、注解、enum

ElementType.CONSTRUCTOR: 构造函数

ElementType.FIELD: 成员变量、对象、属性、枚举的常量

ElementType.LOCAL_VARIABLE: 局部变量

ElementType.METHOD: 方法

ElementType.PACKAGE: 包

ElementType.PARAMETER: 参数

ElementType.ANNOTATION_TYPE): 注解

ElementType.TYPE_PARAMETER:类型参数,表示这个注解可以用在 Type的声明式前,jdk1.8引入。

ElementType.TYPE_USE:类型的注解,表示这个注解可以用在所有使用Type的地方(如:泛型,类型转换等),jdk1.8引入。

Documented

@Documented,表示是否将此注解的相关信息添加到javadoc文档中。


Inherited

@Inherited,定义该注解和子类的关系,使用此注解声明出来的自定义注解,在使用在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。注意,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。


实例

下面通过一个实例来演示注解的使用:通过注解进行赋值和通过注解进行校验。


自定义注解

这里定义两个注解,一个用来赋值,一个用来校验。


/**
 * 性别赋值
 * @author zzs
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface InitSex {
  /**
   * sex enum
   * @author zzs
   */
  enum SEX_TYPE {MAN, WOMAN}
  SEX_TYPE sex() default SEX_TYPE.MAN;
}
/**
 * 年龄校验
 * @author zzs
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {
  /**
   * 最小值
   */
  int min() default 18;
  /**
   * 最大值
   */
  int max() default 99;
  /**
   * 默认值
   */
  int value() default 20;
}

定义数据模型

这里用User类来表示具体待处理的数据对象。

/**
 * user
 *
 * @author zzs
 */
public class User {
  private String username;
  @ValidateAge(min = 20, max = 35, value = 22)
  private int age;
  @InitSex(sex = InitSex.SEX_TYPE.MAN)
  private String sex;
    // 省略getter/setter方法
}

测试调用

具体测试调用的过程,参考代码中的注解,其中initUser方法来演示通过反射给属性赋值,checkUser方法通过反射拿到当前属性的值进行对比校验。

import java.lang.reflect.Field;
/**
 * @author zzs
 */
public class TestInitParam {
  public static void main(String[] args) throws IllegalAccessException {
    User user = new User();
    initUser(user);
    // 年龄为0,校验为通过情况
    boolean checkResult = checkUser(user);
    printResult(checkResult);
    // 重新设置年龄,校验通过情况
    user.setAge(22);
    checkResult = checkUser(user);
    printResult(checkResult);
  }
  static void initUser(User user) throws IllegalAccessException {
    // 获取User类中所有的属性(getFields无法获得private属性)
    Field[] fields = User.class.getDeclaredFields();
    // 遍历所有属性
    for (Field field : fields) {
      // 如果属性上有此注解,则进行赋值操作
      if (field.isAnnotationPresent(InitSex.class)) {
        InitSex init = field.getAnnotation(InitSex.class);
        field.setAccessible(true);
        // 设置属性的性别值
        field.set(user, init.sex().toString());
        System.out.println("完成属性值的修改,修改值为:" + init.sex().toString());
      }
    }
  }
  static boolean checkUser(User user) throws IllegalAccessException {
    // 获取User类中所有的属性(getFields无法获得private属性)
    Field[] fields = User.class.getDeclaredFields();
    boolean result = true;
    // 遍历所有属性
    for (Field field : fields) {
      // 如果属性上有此注解,则进行赋值操作
      if (field.isAnnotationPresent(ValidateAge.class)) {
        ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
        field.setAccessible(true);
        int age = (int) field.get(user);
        if (age < validateAge.min() || age > validateAge.max()) {
          result = false;
          System.out.println("年龄值不符合条件");
        }
      }
    }
    return result;
  }
  static void printResult(boolean checkResult) {
    if (checkResult) {
      System.out.println("校验通过");
    } else {
      System.out.println("校验未通过");
    }
  }
}

打印日志:

完成属性值的修改,修改值为:MAN
年龄值不符合条件
校验未通过
校验通过
• 1
• 2
• 3
• 4

其他实战相关

在具体项目实战中更多的使用拦截器或切面来进行处理,结合具体的场景进行相应的发挥,这里不再赘述。

目录
相关文章
|
10月前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
293 1
|
2月前
|
XML 人工智能 Java
java通过自定义TraceId实现简单的链路追踪
本文介绍了如何在Spring Boot项目中通过SLF4J的MDC实现日志上下文traceId追踪。内容涵盖依赖配置、拦截器实现、网关与服务间调用的traceId传递、多线程环境下的上下文同步,以及logback日志格式配置。适用于小型微服务架构的链路追踪,便于排查复杂调用场景中的问题。
103 0
|
9月前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
191 7
|
10月前
|
Java
在 Java 中,如何自定义`NumberFormatException`异常
在Java中,自定义`NumberFormatException`异常可以通过继承`IllegalArgumentException`类并重写其构造方法来实现。自定义异常类可以添加额外的错误信息或行为,以便更精确地处理特定的数字格式转换错误。
163 1
|
11月前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第17天】本文详细介绍了Java编程中Map的使用,涵盖Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的并发处理和性能优化技巧,适合初学者和进阶者学习。
481 3
|
11月前
|
Java
让星星⭐月亮告诉你,自定义定时器和Java自带原生定时器
定时器是一种可以设置多个具有不同执行时间和间隔的任务的工具。本文介绍了定时器的基本概念、如何自定义实现一个定时器,以及Java原生定时器的使用方法,包括定义定时任务接口、实现任务、定义任务处理线程和使用Java的`Timer`与`TimerTask`类来管理和执行定时任务。
333 3
|
6月前
|
Java 编译器 开发者
注解的艺术:Java编程的高级定制
注解是Java编程中的高级特性,通过内置注解、自定义注解及注解处理器,可以实现代码的高度定制和扩展。通过理解和掌握注解的使用方法,开发者可以提高代码的可读性、可维护性和开发效率。在实际应用中,注解广泛用于框架开发、代码生成和配置管理等方面,展示了其强大的功能和灵活性。
143 25
|
11月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
218 43
Java学习十六—掌握注解:让编程更简单
|
9月前
|
Java
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
257 34
|
9月前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
333 5

热门文章

最新文章