目录
AnnotationParser#parseAnnotations2()
AnnotationParser#annotationForMap()
AnnotationInvocationHandler#invoke()
什么是注解
Java 注解又称 Java 标注,是 JDK5.0 版本开始支持加入源代码的特殊语法元数据 。Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中,Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容。当然它也支持自定义 Java 标注。
注解的分类
元注解
用于描述其他注解的注解,有以下五种常用注解 @Retention、@Target、@Documented、@Inherited、@Repeatable。
@Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
标明自定义注解保留的生命周期,RetentionPolicy 有三个取值:
- SOURCE:注解只在源码中保留,编译时会被丢弃,不会保留在字节码中。
- CLASS:注解在编译时被保留,但JVM运行时不会加载。这是默认的策略。
- RUNTIME:注解会被保留到运行时,因此可以通过反射在运行时访问注解的信息。
@Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
描述自定义注解的作用范围,允许自定义注解标注在哪些Java元素上(类、方法、属性、局部属性、参数…) ,value是一个数组,可以有多个取值,说明同一个注解可以同时用于标注在不同的元素上。
ElementType的取值如下:
- TYPE:类、接口、注解、枚举
- FIELD:属性
- MEHOD:方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造函数
- LOCAL_VARIABLE:局部变量(如循环变量、catch参数)
- ANNOTATION_TYPE:注解
- PACKAGE:包
- TYPE_PARAMETER:泛型参数 jdk1.8
- TYPE_USE:任何元素 jdk1.8
@Documented
是被用来指定自定义注解是否能随着被定义的 java 文件生成到 JavaDoc 文档当中。
@Inherited
是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。同时该注解的@Target的value值必须有
ElementType.TYPE
@Repeatable
是否可以重复标记的意思。下面给一个案例:虽然我们标注的是多个@Car,其实会给我们返回一个@CarList,相当于是Java帮我们把重复的注解放入了一个数组中。
import java.lang.annotation.*;
@Repeatable(value = CarList.class )
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Car {
String name() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CarList {
Car[] value();
}
public class CarAnnotationTest {
@Car(name = "名称一")
@Car(name = "名称二")
public String carName;
public static void main(String[] args) throws NoSuchFieldException {
Class<CarAnnotationTest> carAnnotationTestClass = CarAnnotationTest.class;
Field field = carAnnotationTestClass.getField("carName");
for (Annotation annotation : field.getAnnotations()) {
System.out.println(annotation);
}
}
}

标准注解
Java提供的基础注解,比如下面三个注解。
- @Override 标记一个方法是覆写父类方法
- @Deprecated 标记一个元素为已过期,避免使用
- @SuppressWarnings 不输出对应的编译警告
自定义注解
自定义开发的注解,比如下面注解。
import java.lang.annotation.*;
@Repeatable(value = CarList.class )
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Car {
String name() default "";
}
注解的作用
- 用于标识,比如@Override 标记一个方法是覆写父类方法。
- 编译时生成代码,比如@Data,在字节码文件中生成set和get方法。可以通过继承 AbstractProcessor在编译器做一些事情,具体操作可以参考下面文章:Java注解编译期处理AbstractProcessor详解-腾讯云开发者社区-腾讯云
- 运行时动态处理。通过反射做一些业务处理。
注解的原理
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
@MyAnnotation
public class MyAnnotationTest {
public static void main(String[] args) {
for (Annotation annotation : MyAnnotationTest.class.getAnnotations()) {
System.out.println(annotation.getClass());
}
}
}
结果:可以看出来这是一个JDK动态代理类。
原理解析
Class#getAnnotations()
获取这个类中的所有注解实例,同时通过CAS和自旋的方式保证多线程情况的数据安全。
// 获取注解
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
//
private AnnotationData annotationData() {
while (true) { // retry loop
AnnotationData annotationData = this.annotationData;
int classRedefinedCount = this.classRedefinedCount;
if (annotationData != null &&
annotationData.redefinedCount == classRedefinedCount) {
return annotationData;
}
// 创建AnnotationData
AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);
// CAS的方式更新类中的注解数据
if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {
// successfully installed new AnnotationData
return newAnnotationData;
}
}
}
Class#createAnnotationData
- 将当前类的注解都解析出来
- 获取父类的所有注解,isInherited()判断里面是否有声明为子类可以继承的注解,如果有将这些注解合并到当前类的注解里面一起返回,没有就返回当前类的全部注解。
private AnnotationData createAnnotationData(int classRedefinedCount) {
Map<Class<? extends Annotation>, Annotation> declaredAnnotations =
AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
Class<?> superClass = getSuperclass();
Map<Class<? extends Annotation>, Annotation> annotations = null;
if (superClass != null) {
Map<Class<? extends Annotation>, Annotation> superAnnotations =
superClass.annotationData().annotations;
for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {
Class<? extends Annotation> annotationClass = e.getKey();
if (AnnotationType.getInstance(annotationClass).isInherited()) {
if (annotations == null) { // lazy construction
annotations = new LinkedHashMap<>((Math.max(
declaredAnnotations.size(),
Math.min(12, declaredAnnotations.size() + superAnnotations.size())
) * 4 + 2) / 3
);
}
annotations.put(annotationClass, e.getValue());
}
}
}
if (annotations == null) {
// no inherited annotations -> share the Map with declaredAnnotations
annotations = declaredAnnotations;
} else {
// at least one inherited annotation -> declared may override inherited
annotations.putAll(declaredAnnotations);
}
return new AnnotationData(annotations, declaredAnnotations, classRedefinedCount);
}
AnnotationParser#parseAnnotations2()
该方法的主要功能是从
ByteBuffer 中解析注解信息,涉及到从常量池读取信息、处理各种异常、解析成员信息并存储在
LinkedHashMap 中,最终生成注解对象。它需要处理可能出现的类未定义、类型不存在等异常情况,并根据不同的情况决定是否跳过解析或抛出异常。同时,使用
parseMemberValue 方法解析成员值,将解析结果存储在
LinkedHashMap 中,最终通过
annotationForMap 方法生成最终的注解。
private static Annotation parseAnnotation2(ByteBuffer var0, ConstantPool var1, Class<?> var2, boolean var3, Class<? extends Annotation>[] var4) {
int var5 = var0.getShort() & '\uffff';
Object var6 = null;
String var7 = "[unknown]";
try {
try {
var7 = var1.getUTF8At(var5);
var21 = parseSig(var7, var2);
} catch (IllegalArgumentException var18) {
var21 = var1.getClassAt(var5);
}
} catch (NoClassDefFoundError var19) {
if (var3) {
throw new TypeNotPresentException(var7, var19);
}
skipAnnotation(var0, false);
return null;
} catch (TypeNotPresentException var20) {
if (var3) {
throw var20;
}
skipAnnotation(var0, false);
return null;
}
if (var4 != null && !contains(var4, var21)) {
skipAnnotation(var0, false);
return null;
} else {
Object var8 = null;
try {
var23 = AnnotationType.getInstance(var21);
} catch (IllegalArgumentException var17) {
skipAnnotation(var0, false);
return null;
}
Map var9 = var23.memberTypes();
LinkedHashMap var10 = new LinkedHashMap(var23.memberDefaults());
int var11 = var0.getShort() & '\uffff';
for(int var12 = 0; var12 < var11; ++var12) {
int var13 = var0.getShort() & '\uffff';
String var14 = var1.getUTF8At(var13);
Class var15 = (Class)var9.get(var14);
if (var15 == null) {
skipMemberValue(var0);
} else {
Object var16 = parseMemberValue(var15, var0, var1, var2);
if (var16 instanceof AnnotationTypeMismatchExceptionProxy) {
((AnnotationTypeMismatchExceptionProxy)var16).setMember((Method)var23.members().get(var14));
}
var10.put(var14, var16);
}
}
return annotationForMap(var21, var10);
}
}
AnnotationParser#annotationForMap()
使用JDK的动态代理生成注解对象。
public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) {
return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
public Annotation run() {
return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));
}
});
}
AnnotationInvocationHandler#invoke()
对方法进行增强的逻辑。
- 可以看出这里要求注解中的方法是不可以有参数的
- this.memberValues.get(var4) ,这个 memberValues是一个 Map 类型,内容来自上一步操作解析处理的内容,key是方法名称,value是方法的返回值内容。
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
// 说明除了equals方法,注解中的方法是不能有参数的
throw new AssertionError("Too many parameters for an annotation method");
} else {
switch (var4) {
case "toString":
return this.toStringImpl();
case "hashCode":
return this.hashCodeImpl();
case "annotationType":
return this.type;
default:
// 这块就是对注解中自定义的方法进行增强逻辑,就是通过方法名获取属性值
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}
return var6;
}
}
}
}