0%

Java注解类型@Annotation

Java 注解又称Java标注,是 Java5 版本开始支持加入源代码的特殊语法元数据。 Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容。
Java注解-维基百科

本文介绍了Java注解的申明和定义方法。

基本语法

声明注解与元注解

先给出一个申明注解的例子:

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

@Target@Retention是由Java提供的元注解,所谓元注解就是标记其他注解的注解,下面分别介绍:上述例子使用@interface申明了一个注解Test,并使用@Target注解传入ElementType.METHOD参数来标明@Test只能用于方法上,@Retention(RetentionPolicy.RUNTIME)则用来表示该注解生存期是运行时。

@Target 表示该注解用于什么地方,可能的 ElemenetType 参数包括:

  • ElemenetType.CONSTRUCTOR 构造器声明
  • ElemenetType.FIELD 域声明(包括 enum 实例)
  • ElemenetType.LOCAL_VARIABLE 局部变量声明
  • ElemenetType.METHOD 方法声明
  • ElemenetType.PACKAGE 包声明
  • ElemenetType.PARAMETER 参数声明
  • ElemenetType.TYPE 类,接口(包括注解类型)或enum声明

@Retention 表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括:

  • RetentionPolicy.SOURCE 注解将被编译器丢弃
  • RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃
  • RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。

@Documented 将此注解包含在 javadoc 中

@Inherited 允许子类继承父类中的注解

注解元素及其数据类型

上述所给例子@Test内部没有定义其他元素,所以@Test也称为标记注解(marker annotation),但在自定义注解中 ,一般都会包含一些元素以表示某些值,方便处理器使用,如下例:

1
2
3
4
5
@Target(ElementType.TYPE)//只能应用于类上
@Retention(RetentionPolicy.RUNTIME)//保存到运行时
public @interface DBTable {
String name() default "";
}

其在类上的使用示例为:上述定义一个名为DBTable的注解,我们声明了一个String类型的name元素,其默认值为””空字符。

1
2
3
4
5
//在类上使用该注解
@DBTable(name = "MEMBER")
public class Member {
//.......
}

所有基本类型(int,float,boolean,byte,double,char,long,short)注解中支持的元素数据类型有:

  • String
  • Class
  • enum
  • Annotation
  • 上述类型的数组
    (注解也可以作为注解的元素类型,可以使用嵌套注解;另外,null不能作为元素默认值)

注解编译后自动继承java.lang.annotation.Annotation接口

1
2
3
4
5
6
7
8
package com.zejian.annotationdemo;

import java.lang.annotation.Annotation;
//反编译后的代码
public interface DBTable extends Annotation
{
public abstract String name();
}

注解与反射机制(注:所有的Java注解都继承了Annotation接口)

我们知道Java所有注解都继承了Annotation接口,也就是说 Java使用Annotation接口代表注解元素,该接口是所有Annotation类型的父接口。

同时为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息。

反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口:

Class:类的Class对象定义
Constructor:代表类的构造器定义
Field:代表类的成员变量定义
Method:代表类的方法定义
Package:代表类的包定义

下面是AnnotatedElement中相关的API方法,以上5个类都实现以下的方法:

返回值 方法名称 说明
getAnnotation(Class annotationClass) 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getAnnotations() 返回此元素上存在的所有注解,包括从父类继承的
boolean isAnnotationPresent(Class annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回的数组;

这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组

注解与反射示例

反射注解,那么保留策略必须是Runtime,也就是@Retention(RetentionPolicy.RUNTIME)

①定义一个注解类

1
2
3
4
5
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface MyAnnotation {
int value();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>
1
2
3
4
5
6
7
8
public class MyBean {
@MyAnnotation(20)
private int value;
@Override
public String toString() {
return String.valueOf(value);
}
}

③在main方法里面反射注解②再定义一个类使用注解类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
try {
Field field = MyBean.class.getDeclaredField("value");//获取成员变量value
field.setAccessible(true);//将value设置成可访问的
if(field.isAnnotationPresent(MyAnnotation.class)){//判断成员变量是否有注解
MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);//获取定义在成员变量中的注解MyAnnotation
int value = myAnnotation.value();//获取定义在MyBean的MyAnnotation里面属性值
MyBean myBean=new MyBean();
field.setInt(myBean, value);//将注解的值20可以赋给成员变量value
System.out.println(myBean);//验证结果
}
} catch (Exception e) {
e.printStackTrace();
};
}

注:更多示例请参见:输出结果为:20

深入理解Java注解类型(@Annotation)

Spring注解原理的详细剖析与实现