Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。用到“注释”这个词来描述,不太准备,容易让人误解为,类似于函数注释、属性注释说明一样的功能。和Javadoc不同的是,Java 标注可以通过反射获取标注内容。可以在编译时、运行时、类加载不同时期转换为相应java代码,或者进行字节码文件的修改。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
1.注解
我们先来自己看一下java注解的整体简单架构
从架构图上,先来直接总结一下:
1)Java Annotation 没啥特殊的,只是一个接口
2)我们平常研发过程中,经常见到的,Deprecated, Documented, Inherited, Override等等,是Annotation 实现类而已
3) Annotation 与 ElementType 、RetentionPolicy 从架构图上理解是一种关联的关系,ElementType 与 RetentionPolicy 只是对于 Annotation 的用途或者作用域等的说明
Annotation.java
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
接下来,我们从源码理解、验证、总结一下
1.1 两个属性
1.1.1 ElementType-用途
ElementType 用来指定Annotation 的用途,例如,若一个 Annotation 对象是 METHOD 类型,则该 Annotation 只能用来修饰方法。
`ElementType.java
package java.lang.annotation;
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
//类、接口(包括注释类型)或枚举声明
TYPE,
/** Field declaration (includes enum constants) */
//字段声明(包括枚举常量)
FIELD,
/** Method declaration */
//方法声明
METHOD,
/** Formal parameter declaration */
//方法参数声明
PARAMETER,
/** Constructor declaration */
//构造方法声明
CONSTRUCTOR,
/** Local variable declaration */
//局部变量声明
LOCAL_VARIABLE,
/** Annotation type declaration */
//注解类型声明
ANNOTATION_TYPE,
/** Package declaration */
//包声明
PACKAGE
}
1.1.2 RetentionPolicy -作用域
RetentionPolicy 用来指定Annotation 的作用域,例如,若一个 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。RetentionPolicy.java
package java.lang.annotation;
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* 编译器将Annotation存储于类对应的.class文件中。默认行为
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
* 编译器将Annotation存储于class文件中,并且可由JVM读入
*/
RUNTIME
}
1.2 Java自带的Annotation
1.2.1 meta-annotation 元注解
所谓元注解,其主要作用就是负责注解其他注解
,为其他注解提供了相关的解释说明,我们元注解都在package java.lang.annotation;
下。
@Documented
-- @Documented 所标注内容,可以出现在javadoc中。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Inherited
-- @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
@Retention
-- @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
@Target
-- @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
@Repeatable
--@Repeatable用于声明标记的注解为可重复类型注解,可以在同一个地方多次使用。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
1.2.2 Java自带其他注解
@Deprecated
@Deprecated所标注内容,不再被建议使用。
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@SuppressWarnings
@SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@Override
@Override 只能标注方法,表示该方法覆盖父类中的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
2 Annotation的实际用处
2.1 编译检查
Annotation 具有"让编译器进行编译检查的作用"。
例如,@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用。
package com.itbird.annotation.test;
/**
* OverrideTest测试类
* Created by itbird on 2022/4/12
*/
public class OverrideTest {
/**
* toString() 在java.lang.Object中定义;
* 因此,这里用 @Override 标注是对的。
*/
@Override
public String toString() {
return "OverrideTest toString";
}
/**
* getString() 没有在OverrideTest的任何父类中定义;
* 但是,这里却用 @Override 标注,因此会产生编译错误!
*/
@Override
public String getString() {
return "OverrideTest getString";
}
}
2.2 在反射中使用 Annotation
在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。
这也意味着,我们可以在反射中解析并使用 Annotation。
package com.itbird.annotation.test;
/**
* Created by itbird on 2022/4/12
*/
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Annotation在反射函数中的使用示例
*/
@Retention(RetentionPolicy.RUNTIME)
@interface DefineAnnotation {
String[] value() default "unknown";
}
/**
* Person类。它会使用MyAnnotation注解。
*/
class Person {
/**
* empty()方法同时被 "@Deprecated" 和 "@MDefineAnnotation(value={"a","b"})"所标注
* (01) @Deprecated,意味着empty()方法,不再被建议使用
* (02) @DefineAnnotation, 意味着empty() 方法对应的DefineAnnotation的value值是默认值"unknown"
*/
@DefineAnnotation
@Deprecated
public void empty() {
System.out.println("\nempty");
}
/**
* sombody() 被 @DefineAnnotation(value={"girl","boy"}) 所标注,
*
* @DefineAnnotation(value={"girl","boy"}), 意味着MDefineAnnotation的value值是{"girl","boy"}
*/
@DefineAnnotation(value = {"girl", "boy"})
public void somebody(String name, int age) {
System.out.println("\nsomebody: " + name + ", " + age);
}
}
public class AnnotationReflectTest {
public static void main(String[] args) {
Person person = new Person();
try {
// 获取 somebody() 方法的Method实例
Method somebodyMethod = Person.class.getDeclaredMethod("somebody", new Class[]{String.class, int.class});
// 获取 somebodyMethod的注解
iteratorAnnotation(somebodyMethod);
// 执行 somebodyMethod方法
somebodyMethod.invoke(person, new Object[]{"itbird", "18"});
// 获取 empty() 方法的Method实例
Method emptyMethod = Person.class.getDeclaredMethod("empty", null);
// 获取 emptyMethod的注解
iteratorAnnotation(emptyMethod);
// 执行 emptyMethod
emptyMethod.invoke(person, null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private static void iteratorAnnotation(Method somebodyMethod) {
//方法是否拥有注解
if (somebodyMethod.isAnnotationPresent(DefineAnnotation.class)) {
//获取方法的所有注解
DefineAnnotation annotation = (DefineAnnotation) somebodyMethod.getAnnotation(DefineAnnotation.class);
// 循环打印注解
String[] values = annotation.value();
for (String str : values)
System.out.printf(str + ", ");
System.out.println();
}
}
}
2.3 根据 Annotation 生成帮助文档
通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中。
2.4 能够帮忙查看查看代码
通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。