Java注解使用和原理分析

目录

什么是注解

注解的分类

元注解

@Retention

@Target

@Documented

@Inherited

@Repeatable

标准注解

自定义注解

注解的作用

注解的原理

原理解析

Class#getAnnotations()

Class#createAnnotationData

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 "";
}

注解的作用

注解的原理

        注解本质是一个继承了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

  1. 将当前类的注解都解析出来
  2. 获取父类的所有注解,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;
                }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值