注解(Annotation) 是 Java 5引入的一种代码元数据形式,用于为程序提供信息,可以在编译、类加载、运行时被读取,并进行相应处理。
生成文档:通过代码里标识的元数据生成 javadoc 文档。
编译检查:通过代码里标识的元数据让编译器在编译期间进行检查验证。
编译时动态处理:编译时通过代码里标识的元数据动态处理。
运行时动态处理:运行时通过代码里标识的元数据动态处理。
1 Java 内置注解 Java 1.5 开始自带的标准注解,包括 @Override
、@Deprecated
和 @SuppressWarnings
:
1.1 @Override @Override
:$\text {java.lang.Override}$
@Target(ElementType.METHOD)
:仅能用于方法。
@Retention(RetentionPolicy.SOURCE)
:仅在源代码级别有效,编译后不会保留在字节码中。
1 2 3 4 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override {}
1.2 @Deprecated @Deprecated
:$\text {java.lang.Deprecated}$
@Documented
:会被 javadoc 工具记录到文档中。
@Retention(RetentionPolicy.RUNTIME)
:会保留到运行时,可以通过反射获得。
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
:构造函数,字段,局部变量,方法,包,方法参数,类、接口、枚举。
1 2 3 4 5 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated {}
1.3 @SuppressWarnings @SuppressWarnings
:$\text {java.lang.SuppressWarnings}$
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
:类、接口、枚举,字段,方法,方法参数,构造函数,局部变量。
@Retention(RetentionPolicy.SOURCE)
:仅在源代码级别有效,编译后不会保留在字节码中。
1 2 3 4 5 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
2 元注解 上述内置注解定义中使用了部分元注解。
按照 JDK 版本更新顺序列出 Java 的元注解:
JDK 5.0 引入的元注解:@Target
,@Retention
,@Documented
,@Inherited
。
JDK 8.0 新增:@Repeatable
。
2.1 @Target $\text {java.lang.annotation.Target}$
@Target
- 说明注解可以应用的目标类型:限定注解可以用于哪些 Java 元素类型。
常用值:METHOD, FIELD, TYPE, PARAMETER 等
1 2 3 4 5 6 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
可以修饰的对象 $\text {java.lang.annotation.ElementType}$:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }
2.2 @Retention $\text {java.lang.annotation.Retention}$
@Retention
- 说明注解的生命周期:指定注解信息保留到什么时候。
$\text {java.lang.annotation.RetentionPolicy}$:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
示例:验证应用了三种策略的注解类区别。
1 2 @Retention(RetentionPolicy.SOURCE) public @interface SourcePolicy {}
1 2 @Retention(RetentionPolicy.CLASS) public @interface ClassPolicy {}
1 2 @Retention(RetentionPolicy.RUNTIME) public @interface RuntimePolicy {}
使用定义的注解类去注解不同的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class RetentionTest { @SourcePolicy public void sourcePolicy () { } @ClassPolicy public void classPolicy () { } @RuntimePolicy public void runtimePolicy () { } }
编译所有文件:
反编译 RetentionTest.class 文件:
1 $ javap -verbose RetentionTest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class RetentionTest { public RetentionTest () { } public void sourcePolicy () { } @ClassPolicy public void classPolicy () { } @RuntimePolicy public void runtimePolicy () { } }
编写测试代码验证运行时的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { Method[] methods = RetentionTest.class.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName()); Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.annotationType().getName()); } System.out.println("-------------------" ); } } runtimePolicy RuntimePolicy ------------------- sourcePolicy ------------------- classPolicy -------------------
总结三种策略的生命周期可见性:
注解
编译阶段
字节码阶段
运行时阶段
SOURCE
✅
❌
❌
CLASS
✅
✅
❌
RUNTIME
✅
✅
✅
2.3 @Documented $\text {java.lang.annotation.Documented}$
@Documented
- 说明注解是否包含在 Javadoc 中:被该元注解修饰的注解会出现在生成的文档中。
1 2 3 4 5 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented {}
示例:
1 2 3 4 5 6 7 8 9 10 @Documented @Retention(RetentionPolicy.RUNTIME) public @interface DocumentedAnnotation { String value () default "" ; } @Retention(RetentionPolicy.RUNTIME) public @interface UndocumentedAnnotation { String value () default "" ; }
1 2 3 4 5 6 7 8 @DocumentedAnnotation("Class annotation with documentation") @UndocumentedAnnotation("Class annotations without documentation") public class DocumentedTest { @DocumentedAnnotation("Method annotation with documentation") @UndocumentedAnnotation("Method annotation without documentation") public void testMethod () { } }
1 $ javadoc -d doc DocumentedTest.java DocumentedAnnotation.java UndocumentedAnnotation.java
在当前目录下生成一个名为 “doc” 的文件夹,用浏览器打开 doc/index.html。
带有 @Documented 的注解(DocumentedAnnotation)会显示在文档中
不带 @Documented 的注解(UndocumentedAnnotation)不会显示在文档中
2.4 @Inherited $\text {java.lang.annotation.Inherited}$
@Inherited
- 说明注解是否可以被继承:允许子类继承父类的注解。
1 2 3 4 5 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited {}
示例:
1 2 3 4 5 6 7 8 9 10 @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface InheritableAnnotation { String value () default "" ; } @Retention(RetentionPolicy.RUNTIME) public @interface UnInheritableAnnotation { String value () default "" ; }
1 2 3 4 5 6 7 @InheritableAnnotation("Inheritable Annotations") @UnInheritableAnnotation("NonInheritable Annotations") public class Parent {} public class Child extends Parent {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static void main (String[] args) { Annotation[] parentAnnotations = Parent.class.getAnnotations(); System.out.println("parent class annotations: " ); for (Annotation annotation : parentAnnotations) { System.out.print(" " + annotation); } System.out.println(); Annotation[] childAnnotations = Child.class.getAnnotations(); System.out.println("child class annotations: " ); for (Annotation annotation : childAnnotations) { System.out.print(" " + annotation); } System.out.println(); System.out.println("inheritable annotation: " + Child.class.isAnnotationPresent(InheritableAnnotation.class)); System.out.println("uninheritable annotation: " + Child.class.isAnnotationPresent(UnInheritableAnnotation.class)); } parent class annotations : @InheritableAnnotation(value=Inheritable Annotations) @UnInheritableAnnotation(value=NonInheritable Annotations) child class annotations : @InheritableAnnotation(value=Inheritable Annotations) inheritable annotation: true uninheritable annotation: false
2.5 @Repeatable $\text {java.lang.annotation.Repeatable}$
@Repeatable
- 说明注解是否可重复使用:允许在同一个位置重复使用同一个注解。
1 2 3 4 5 6 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Repeatable { Class<? extends Annotation > value(); }
$\text {java.lang.reflect.AnnotatedElement}$:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 boolean isAnnotationPresent (Class<? extends Annotation> annotationClass) <T extends Annotation > T getAnnotation (Class<T> annotationClass) Annotation[] getAnnotations() <T extends Annotation > T[] getAnnotationsByType(Class<T> annotationClass) <T extends Annotation > T getDeclaredAnnotation (Class<T> annotationClass) Annotation[] getDeclaredAnnotations() <T extends Annotation > T[] getDeclaredAnnotationsByType(Class<T> annotationClass)
3 自定义注解 @Retention、@Documented、@Inherited 和 @Repeatable 的相关示例都展示了基于元注解来自定义注解。
4 注解实现原理 示例:展示 Java 注解的基本实现原理,包含:
4.1 注解定义 @LogExecutionTime
注解,用于记录方法执行时间。
1 2 3 4 5 6 7 8 9 10 import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecutionTime { String value () default "" ; }
4.2 注解使用 1 2 3 public interface IUserService { void getUserInfo (String userId) ; }
1 2 3 4 5 6 7 8 9 10 11 public class UserService implements IUserService { @LogExecutionTime("getUserInfo") public void getUserInfo (String userId) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Getting user info for: " + userId); } }
4.3 动态代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class LogExecutionTimeProxy implements InvocationHandler { private final Object target; public LogExecutionTimeProxy (Object target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes()); if (targetMethod.isAnnotationPresent(LogExecutionTime.class)) { long startTime = System.currentTimeMillis(); Object result = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println("Method " + method.getName() + " took " + (endTime - startTime) + "ms to execute" ); return result; } return method.invoke(target, args); } public static Object createProxy (Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LogExecutionTimeProxy (target) ); } }
4.4 结果演示 1 2 3 4 5 6 7 8 9 public class Main { public static void main (String[] args) { IUserService userService = (IUserService) LogExecutionTimeProxy.createProxy(new UserService ()); userService.getUserInfo("2025" ); } } Getting user info for : 2025 Method getUserInfo took 1010ms to execute