注解【开发实践】

一、注解概述

1.1 什么是注解

注解(Annotation)是编程语言中的一种元数据形式,它提供了一种机制,可以在不直接影响程序代码执行逻辑的情况下,向源代码中添加信息。注解不会直接改变程序的行为,而是作为一种标记或指令,供编译器、开发工具或运行时环境解析和使用,以实现特定的功能或处理。

简单来说,使用注解标注相当于在代码的某个位置打上标签,标签上有一些元数据,仅此而已。注解本身不改变程序行为,需要通过自定义的注解处理器来扫描注解,并在扫描到注解后进行预设的处理。

1.2 注解的作用

  • 生成文档:注解可以用来生成API文档,如Java中的@param、@return等,帮助自动生成详细的文档说明。
  • 编译时检查:例如,@Override注解告诉编译器该方法意在重写超类的方法,如果实际上没有匹配的方法,则编译器会报错。
  • 编译时处理:注解处理器可以在编译阶段读取注解信息,并据此生成额外的源代码或资源文件,如Lombok的@Data自动为类生成getter和setter方法。
  • 运行时处理:某些注解在程序运行时会被解析,用于动态地决定程序行为,如Spring框架中使用注解来配置依赖注入、事务管理等。
  • 代码分析和框架集成:注解可以帮助工具和框架识别代码结构,如单元测试框架识别测试方法,或IDE识别需要特殊处理的代码块。

1.3 一些特殊的注解

标记注解:没有元素的注解。
单值注解:只有一个元素的注解,设置元素值时可以省略元素名。
元注解:作用于注解的注解。
容器注解:为定义可重复使用注解而定义的辅助注解。

二、元注解

元注解是Java中一种特殊的注解,它们主要用于注解其他的注解,为这些注解提供元数据级别的信息,如生命周期、目标位置、是否可继承等。Java自身定义了几个标准的元注解,它们位于java.lang.annotation包中。

2.1 @Retention

指定被修饰的注解的保留策略,即指定注解的生命周期。它有一个枚举类型的参数RetentionPolicy,可选值有:

  • SOURCE:注解只保留在源码中,编译时会被忽略。
  • CLASS(默认):注解会保留在class文件中,但运行时不会被虚拟机保留,因此无法在运行时通过反射访问。
  • RUNTIME:注解会保留在class文件中,并且能通过反射在运行时访问。

2.2 @target

限定被修饰的注解能够应用到哪些程序元素上。它的参数是一个枚举类型ElementType,可选值包括TYPE(类,接口,枚举)、METHOD(方法)、FIELD(字段)、PARAMETER(参数)、CONSTRUCTOR(构造器)、LOCAL_VARIABLE(局部变量)、ANNOTATION_TYPE(注解)、PACKAGE(包)等。

如果想将注解用于与多种程序元素上,可以使用多个值,这些值之间使用逗号隔开。默认情况下(没有指定时)可以应用于上述所有程序元素。

2.3 @Documented

标记被修饰的注解,使其在生成JavaDoc文档时包含进去。

简单来说,如果一个注解被@Documented修饰,那么这个注解所标注的元素的API文档中将展示这个注解,否则不会展示。

2.4 @Inherited

标记一个注解,表示该注解支持子类继承(仅对类继承有效,不适用于接口继承)。当一个类上加了该注解后,该注解也会隐式地应用于这个类的所有子类。

2.5 @Repeatable

表示某个注解可以重复使用在同一个目标上,此种注解的定义需要配合容器注解来使用,容器注解用于存储可重复注解的实例数组(详情见后)。

三、注解的使用

3.1 定义注解

定义注解就像定义接口,但有所区别。关键字换成了@interface,实现体中类似定义"方法"的形式来定义元素:"返回值"类型即元素类型,"方法名"即元素名,注意没有参数和方法体。对于每个元素,可以使用default来设置默认值。

定义不可重复使用的注解。

public @interface MyAnnotation {

    // 没有默认值的情况,使用时必须提供
    String requiredValue();
    
    // 有默认值的情况,使用时可以不提供
    String optionalValue() default "Default Value";
}

定义可重复使用的注解

// 定义可重复使用的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// 使用@Repeatable标记,表明是可重复使用的注解,使用属性指对应的容器注解。
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
    String value();
}

// 定义容器注解,就是一种特殊的注解,用于存储可重复使用注解的实例数组。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotations {
	// 可重复使用注解的数组类型
    MyAnnotation[] myAnnotations();
}

3.2 编写注解处理器

编写注解处理器,需要实现javax.annotation.processing.Processor接口。最简单的实现可以继承AbstractProcessor 抽象类来实现,其中process()方法是必须实现的,负责实际的注解处理逻辑。

process的参数
annotations:注解处理器所负责的注解的集合。
roundEnvironment:提供了访问注解所应用元素的环境。

process的返回值
返回true表示该处理器已经处理了所负责的所有注解,不需要其他处理器再处理这些注解。返回false则表示后续处理器还可以尝试处理这些注解。在大多数情况下,如果处理器完成了它的工作,应该返回true。

// 指定注解处理器需要处理的注解
@SupportedAnnotationTypes("你的包名.自定义注解名")
// 指定注解处理器支持的Java语言版本。
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        // 初始化处理环境,可在此处设置工具、日志等
    }

     @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    	// 遍历需要处理的注解
        for (TypeElement annotation : annotations) {
        	// 对于每个注解,获取其标注的所有对象
            Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
            // 遍历注解标注的对象,进行相应的处理
            for (Element element : annotatedElements) {
                ...
            }
        }
        return true;
    }
}

3.3 注册注解处理器

确保你的处理器能够被Java编译器发现,通常这需要在项目的META-INF/services/javax.annotation.processing.Processor文件中声明处理器的全限定名。如果你使用的是Maven或Gradle等构建工具,通常有相应的配置方式来自动注册处理器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值