1

什么是注解?

Annotation 是 Java5 之后开始引入的新特性,中文为注解。注解提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明与程序的业务逻辑无关,并且提供给指定的工具或框架使用。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

总结

==注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记==,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,==标记可以加在包、类,属性、方法,方法的参数以及局部变量上。==

一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。

元注解(meta-annotation)

元注解的作用就是负责注解其他注解。 Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。他们分别是 @Target、@Retetion、@Documented、@Inherited

@Target 注解

@Target 的作用:就是说明注解可以用在什么地方。

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

ElementType 的值有以下几种:

1.CONSTRUCTOR: 用于描述构造器
2.FIELD: 用于描述域
3.LOCAL_VARIABLE: 用于描述局部变量
4.METHOD: 用于描述方法
5.PACKAGE: 用于描述包
6.PARAMETER: 用于描述参数
7.TYPE: 用于描述类、接口(包括注解类型) 或enum声明

代码示例

// 下面的注解可以作用在字段上面
@Target(ElementType.FIELD)
public @interface MyAnnotation {
}

@Retention 注解

@Retention 注解决定 注解的生命周期。我们的java程序执行的顺序: Java源文件(x.java文件) ---> .class(编译为 class 文件) ---> 内存中的字节码文件(运行时)。

对应于 @Retention 注解的三个值:

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留)。在编译阶段丢弃,这些注解在编译结束之后就没有任何意义了。
  • RetentionPolicy.CLASS:在class文件中有效(即class保留)。在类加载的时候丢弃。注解默认是使用这种方式。
  • RetentionPolicy.RUNTIME:在运行时有效(即运行时保留)。始终不丢弃,运行期也保留该注解。因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

@Documented 注解

一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。

@Inherited 注解

 @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
  

为注解增加属性

value 属性

value 属性是一个特殊的属性。当注解中使用的属性名为value时,对其赋值时可以不指定属性名称而直接写上属性值接口;除了value以外的变量名都需要使用 name = value 的方式赋值。

示例

@SuppressWarnings("deprecation")    // 在使用的地方

给属性设置初始值

即在定义注解的时候,就给某些字段附上初始值。

示例

String value() default "config";    // 在自定义注解的地方

数组类型的属性

int[] intArray() default {1,2,3};    // 在自定义注解的地方

CustomAnnotation(ingArray = {4,5,6})    // 在使用注解的地方

CustomAnnotation(ingArray = 7)          // 如果数组属性中只有一个元素时,属性值部分可以省略大括号。

java 8 对注解的增强

@Repeatable注解,关于类型注解的声明,函数式接口注解@FunctionalInterface(与Lambdas结合使用)。@Repeatable:说明该注解标识的注解可以多次使用到同一个元素的声明上。

注解的重复机制

@Repeatable(Annotations.class) 
public @interface MyAnnotation {  
     String role();  
}  
 
public @interface Annotations {  
    MyAnnotation[] value();  
}  
 
public class RepeatAnnotationUseOldVersion {  
    @MyAnnotation(role="Admin")  
    @MyAnnotation(role="Manager")
    public void doSomeThing(){  
    }  
} 

自定义注解

注解的声明方式

@Retention(value=RUNTIME)    // value的值是可以改变的, RUNTIME 只是作为demo
@Target(value=TYPE)          // value的值是可以改变的, TYPE 只是作为demo
public @interface CustomAnnotationClass{}

对于注解的声明,由于 java 没有引入新的关键字,而是使用 @interface 关键字,来声明一个注解。我们需要为自定义的注解增加强制性的属性,保留策略和作用的目标。这样子声明的直接才有意义。

实例说明

CustomAnnotation.java

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


@Retention(value=RUNTIME)     // RUNTIME 告诉程序这个注解不会被丢弃,可以通过反射来获取
@Target(value=METHOD)   // 通过 METHOD 属性,可以让注解在方法上面
public @interface CustomAnnotation {

    /* value:是一个特殊的属性,若在设置值时只有一个value属性需要设置或者其他属性都采用默认值时 ,
    那么value=可以省略,直接写所设置的值即可。 */
    String value(); // 注解只要一个变量时,变量名必须为 value

    /* 我们所有的方法声明都不能有参数和throw 子句 */
    public String name() default "Wayfreem";

    public String description();
}

获取注解

我们获取注解一般通过反射的方式来获取。java 反射API提供了许多方法,在运行时从类、方法获取其他元素上面获取注解信息。下面列出常用的方法:

  • getAnnotations(): 返回该元素的所有注解,包括没有显式定义该元素上的注解。
  • isAnnotationPresent(annotation): 检查传入的注解是否存在于当前元素。
  • getAnnotation(class): 按照传入的参数获取指定类型的注解。返回null说明当前元素不带有此注解。返回值为Annotation[]。
  • getMethod(String name, Class<?>... parameterTypes):获取某个方法
public class TestAnnotation {

    @CustomAnnotation(value = "test1", name = "测试1", description = "测试方法1")
    private void method1(){}

    @CustomAnnotation(value = "test2", name = "测试2", description = "测试方法2")
    public void method2(){}

    @CustomAnnotation(value = "test3", name = "测试3", description = "测试方法3")
    public void method3(){}

    public void menthod4(){}

    public static void main(String[] args) {

        // 通过反射来回去所有声明的方法(包含还有加上了注解了的和未注解的 )
        Method[] methods = TestAnnotation.class.getDeclaredMethods();

        for (Method method: methods){

            // 传入指定的注解,判断是否为传入的注解类型
            boolean flag = method.isAnnotationPresent(CustomAnnotation.class);
            if(!flag){
                continue;
            }

            // 获取到指定的注解类型,并且将其实例化
            CustomAnnotation customAnnotation = method.getAnnotation(CustomAnnotation.class);
            // 获取到自定义的注解中的值
            System.out.println("value : "+customAnnotation.value() + 
                    ", name:" + customAnnotation.name() + 
                    ", description:"+ customAnnotation.description());
        }
    }
}

控制台输出

value : test1, name:测试1, description:测试方法1
value : test2, name:测试2, description:测试方法2
value : test3, name:测试3, description:测试方法3

Wayfreem
241 声望33 粉丝

一个后端工程师,偏偏喜欢前端。