自定义注解 原理详解

目录

使用方式简介

新建注解类

package com.aya;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Star {
    String value() default "hello";
}

新建被注解标注的类

@Star
public class Student {
}

新建测试方法

    @Test
    public void test(){
        Student student = new Student();
        Star annotation = Student.class.getAnnotation(Star.class);
        System.out.println(annotation.value());
    }

控制台输出: hello

注解是什么

用 javap -c -p com/aya/Star

public interface com.aya.Star extends java.lang.annotation.Annotation {
  public abstract java.lang.String value();
}

得出结论:注解就是一个继承 Annotation 的一个接口

注解的实现类是什么

在本例中,注解的对象是通过 Student.class.getAnnotation(Star.class); 获取到的

也就是分析 Class.getAnnotation 如何创建注解接口的实现类的对象的

第一节

1.1 获取Annotation

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        Objects.requireNonNull(annotationClass);

        return (A) annotationData().annotations.get(annotationClass);
    }
  1. 这里首先验证 annotationClass 不为 null
  2. 获取 java.lang.reflect.Class.AnnotationData 内部类 实例
  3. 用 AnnotationData 实例的 (Map对象)annotations 获取 key

也就是说 在执行 annotationData() 的时候,对象已经创建完成了

在第一阶段,就需要找到 annotationData().annotations 在哪里赋值的就结束了

1.2 获取AnnotationData

    private AnnotationData annotationData() {
        while (true) { // retry loop
            AnnotationData annotationData = this.annotationData;
            int classRedefinedCount = this.classRedefinedCount;
            if (annotationData != null &&
                annotationData.redefinedCount == classRedefinedCount) {
                return annotationData;
            }
            // null or stale annotationData -> optimistically create new instance
            AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);
            // try to install it
            if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {
                // successfully installed new AnnotationData
                return newAnnotationData;
            }
        }
    }
  1. 当前Class实例的annotationData 不为空就返回
  2. 为空就创建 newAnnotationData
  3. 原子替换this.annotationData = newAnnotationData,替换成功就返回 newAnnotationData

第一次获取注解时 this.annotationData 一定为null,当获取某个注解之后, this.annotationData 被填充

接下来就分析如何创建注解数据 createAnnotationData(classRedefinedCount);

1.3 创建AnnotationData

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

上面代码有些长,不过可以分为以下几个步骤去分析
1. AnnotationParser.parseAnnotations 获取 类的所有注解实现Class和注解实现类的映射
2. 获取父类,Student的父类 java.lang.Object
3. 如果父类注解不为空,就遍历父类注解做某事.
4. 从父类获取的注解为空,就返回当前类的所有注解,否则,用用父类的注解map填充自己的注解.
5. 创建 AnnotationData 实例,然后返回

在第4步里面, 父类映射.putAll(子类映射) ,这样的话: 当子类和父类拥有同样的注解时,子类会覆盖父类注解

在第一阶段,我们要获取的注解Class对象和注解的对象的映射 declaredAnnotations 已经获取到了

接下来进入第二阶段: 分析 declaredAnnotations 的来源

第二节

在这里的时候,jdk就已经不提供源码了

我们将 jdk/jre/lib/rt.jar 中的 sun.reflect.annotation.AnnotationParser.class 解压出来

用 java -jar cfr_0_131.jar AnnotationParser.class –methodname parseAnnotations 得到下面的代码

后文不在赘述 cfr 使用方式

2.1 解析注解映射

public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(byte[] arrby, ConstantPool constantPool, Class<?> class_) {
    if (arrby == null) {
        return Collections.emptyMap();
    }
    try {
        return AnnotationParser.parseAnnotations2(arrby, constantPool, class_, null);
    }
    catch (BufferUnderflowException bufferUnderflowException) {
        throw new AnnotationFormatError("Unexpected end of annotations.");
    }
    catch (IllegalArgumentException illegalArgumentException) {
        throw new AnnotationFormatError(illegalArgumentException);
    }
}

当注解 Star 没有加入注解: @Retention(RetentionPolicy.RUNTIME) 时,参数 arrby 为 null

2.2 这里仅仅是进行操作之前做了验证和异常捕获,接着分析 parseAnnotations2

  private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(byte[] paramArrayOfByte, ConstantPool paramConstantPool, Class<?> paramClass, Class<? extends Annotation>[] paramArrayOfClass)
  {
    LinkedHashMap localLinkedHashMap = new LinkedHashMap();

    ByteBuffer localByteBuffer = ByteBuffer.wrap(paramArrayOfByte);
    int i = localByteBuffer.getShort() & 0xFFFF;
    for (int j = 0; j < i; j++)
    {
      Annotation localAnnotation = parseAnnotation2(localByteBuffer, paramConstantPool, paramClass, false, paramArrayOfClass);
      if (localAnnotation != null)
      {
        Class localClass = localAnnotation.annotationType();
        if ((AnnotationType.getInstance(localClass).retention() == RetentionPolicy.RUNTIME) && 
          (localLinkedHashMap.put(localClass, localAnnotation) != null)) {
          throw new AnnotationFormatError("Duplicate annotation for class: " + localClass + ": " + localAnnotation);
        }
      }
    }
    return localLinkedHashMap;
  }

到这里就找到了 Map 的实现类 LinkedHashMap

那么它的值又依赖 parseAnnotation2 的返回结果 localAnnotation

接下来进行第三阶段的分析: Annotation 对象的获取

第三节

3.1 解析注解映射

private static Annotation parseAnnotation2(ByteBuffer byteBuffer, ConstantPool constantPool, Class<?> class_, boolean bl, Class<? extends Annotation>[] arrclass) {
    int n = byteBuffer.getShort() & 65535;
    Class<?> class_2 = null;
    String string = "[unknown]";
    try {
        try {
            string = constantPool.getUTF8At(n);
            class_2 = AnnotationParser.parseSig(string, class_);
        }
        catch (IllegalArgumentException illegalArgumentException) {
            class_2 = constantPool.getClassAt(n);
        }
    }
    catch (NoClassDefFoundError noClassDefFoundError) {
        if (bl) {
            throw new TypeNotPresentException(string, noClassDefFoundError);
        }
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }
    catch (TypeNotPresentException typeNotPresentException) {
        if (bl) {
            throw typeNotPresentException;
        }
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }
    if (arrclass != null && !AnnotationParser.contains(arrclass, class_2)) {
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(class_2);
    }
    catch (IllegalArgumentException illegalArgumentException) {
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }
    Map<String, Class<?>> map = annotationType.memberTypes();
    LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<String, Object>(annotationType.memberDefaults());
    int n2 = byteBuffer.getShort() & 65535;
    for (int i = 0; i < n2; ++i) {
        int n3 = byteBuffer.getShort() & 65535;
        String string2 = constantPool.getUTF8At(n3);
        Class<?> class_3 = map.get(string2);
        if (class_3 == null) {
            AnnotationParser.skipMemberValue(byteBuffer);
            continue;
        }
        Object object = AnnotationParser.parseMemberValue(class_3, byteBuffer, constantPool, class_);
        if (object instanceof AnnotationTypeMismatchExceptionProxy) {
            ((AnnotationTypeMismatchExceptionProxy)object).setMember(annotationType.members().get(string2));
        }
        linkedHashMap.put(string2, object);
    }
    return AnnotationParser.annotationForMap(class_2, linkedHashMap);
}

这段代码比较长, 再次分为多个阶段进行解读

3.1.1 创建注解Class对象
    try {
        try {
            string = constantPool.getUTF8At(n);
            class_2 = AnnotationParser.parseSig(string, class_);
        }
        catch (IllegalArgumentException illegalArgumentException) {
            class_2 = constantPool.getClassAt(n);
        }
    }
    catch (NoClassDefFoundError noClassDefFoundError) {
        if (bl) {
            throw new TypeNotPresentException(string, noClassDefFoundError);
        }
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }
    catch (TypeNotPresentException typeNotPresentException) {
        if (bl) {
            throw typeNotPresentException;
        }
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }
    if (arrclass != null && !AnnotationParser.contains(arrclass, class_2)) {
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }

获取Class对象
1. 从常量池中获取指定位置的类文件名,本例为: Lcom/aya/Star;
2. 通过类文件名创建Class对象

3.1.2 获取AnnotationType对象,获得注解默认值
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(class_2);
    }
    catch (IllegalArgumentException illegalArgumentException) {
        AnnotationParser.skipAnnotation(byteBuffer, false);
        return null;
    }

通过Class对象创建接口 com.aya.Star的实例
1. 通过Class 获取 AnnotationType 的实例
2. 获取注解的所有方法和默认值的Map映射

3.1.3 覆盖注解默认值
    Map<String, Class<?>> map = annotationType.memberTypes();
    LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<String, Object>(annotationType.memberDefaults());
    int n2 = byteBuffer.getShort() & 65535;
    for (int i = 0; i < n2; ++i) {
        int n3 = byteBuffer.getShort() & 65535;
        String string2 = constantPool.getUTF8At(n3);
        Class<?> class_3 = map.get(string2);
        if (class_3 == null) {
            AnnotationParser.skipMemberValue(byteBuffer);
            continue;
        }
        Object object = AnnotationParser.parseMemberValue(class_3, byteBuffer, constantPool, class_);
        if (object instanceof AnnotationTypeMismatchExceptionProxy) {
            ((AnnotationTypeMismatchExceptionProxy)object).setMember(annotationType.members().get(string2));
        }
        linkedHashMap.put(string2, object);
    }
  1. 确保buffer 在short 范围内 ( int 类型的任意值 & 0xFFFF 一定是一个short 值)
  2. 从常量池获取指定位置的内容(获取用户赋值的方法名)
  3. 获取方法返回值类型
  4. AnnotationParser.parseMemberValue 获取用户设置的值
  5. 覆盖默认值

3.2 创建实现类Proxy

    public static Annotation annotationForMap(final Class<? extends Annotation> class_, final Map<String, Object> map) {
        return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>(){

            @Override
            public Annotation run() {
                return (Annotation)Proxy.newProxyInstance(class_.getClassLoader(), new Class[]{class_}, new AnnotationInvocationHandler(class_, map));
            }
        });
    }

通过Proxy创建动态代理
本例中,newProxyInstance 的三个参数
1. classLoader: AppClassLoader
2. interfaces: com.aya.Star
3. invocationHandler: AnnotationInvocationHandler

直到这里才能得出结论: 注解的实现类是Proxy的子类

本文只是注解详解,并不是动态代理详解,所以这里不进行如何创建代理对象的分析

注解如何执行方法

在创建注解对象的时候,通过构造器,传入注解的 Class对象和 (方法名和值)的一个Map

构造

 AnnotationInvocationHandler(Class<? extends Annotation> paramClass, Map<String, Object> paramMap)
  {
    Class[] arrayOfClass = paramClass.getInterfaces();
    if ((!paramClass.isAnnotation()) || (arrayOfClass.length != 1) || (arrayOfClass[0] != Annotation.class))
    {
      throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type."); 
    }
    type = paramClass;
    memberValues = paramMap;
  }
  1. 存储注解的Class对象
  2. 存储注解解析后的方法名和值的Map

在经历了 第三节-3.1.3 之后这里得到的已经是用户设置的值了

执行

解析AnnotationInvocationHandler 的invoke


  public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) {
    String str = paramMethod.getName();
    Class[] arrayOfClass = paramMethod.getParameterTypes();


    if ((str.equals("equals")) && (arrayOfClass.length == 1) && (arrayOfClass[0] == Object.class))
    {
      return equalsImpl(paramArrayOfObject[0]); 
    }
    if (arrayOfClass.length != 0) {
      throw new AssertionError("Too many parameters for an annotation method");
    }
    Object localObject = str;
    int i = -1; 
    switch (((String)localObject).hashCode()) {
    case -1776922004:  
        if (((String)localObject).equals("toString"))  i = 0; break; 
    case 147696667:  
        if (((String)localObject).equals("hashCode")) i = 1; break; 
    case 1444986633:  
        if (((String)localObject).equals("annotationType")) i = 2; break; 
    } 
    switch (i) {
    case 0: 
      return toStringImpl();
    case 1: 
      return Integer.valueOf(hashCodeImpl());
    case 2: 
      return type;
    }


    localObject = memberValues.get(str);

    if (localObject == null) {
      throw new IncompleteAnnotationException(type, str);
    }
    if ((localObject instanceof ExceptionProxy)) {
      throw ((ExceptionProxy)localObject).generateException();
    }
    if ((localObject.getClass().isArray()) && (Array.getLength(localObject) != 0)) {
      localObject = cloneArray(localObject);
    }
    return localObject;
  }

方法调用分以下四种情况
1. equals 内置处理
2. hashCode 内置处理
3. annotationType 返回注解的Class对象
4. 用户自定义方法时: 获取方法名对应的值

内置处理

toStringImpl

private String toStringImpl() {
    StringBuilder stringBuilder = new StringBuilder(128);
    stringBuilder.append('@');
    stringBuilder.append(this.type.getName());
    stringBuilder.append('(');
    boolean bl = true;
    for (Map.Entry<String, Object> entry : this.memberValues.entrySet()) {
        if (bl) {
            bl = false;
        } else {
            stringBuilder.append(", ");
        }
        stringBuilder.append(entry.getKey());
        stringBuilder.append('=');
        stringBuilder.append(AnnotationInvocationHandler.memberValueToString(entry.getValue()));
    }
    stringBuilder.append(')');
    return stringBuilder.toString();
}

按照 @(key=value,key1=value1) 的形式,返回注解的所有方法名和返回值

hashCodeImpl

private int hashCodeImpl() {
    int n = 0;
    for (Map.Entry<String, Object> entry : this.memberValues.entrySet()) {
        n += 127 * entry.getKey().hashCode() ^ AnnotationInvocationHandler.memberValueHashCode(entry.getValue());
    }
    return n;
}

127*key1.hashCode+value1.hashCode + 127*key2.hashCode+value2.hashCode ... 的形式,生成一个hashCode

总结

疑问

  1. 为什么在自定义注解上加: @Retention(RetentionPolicy.RUNTIME) ,参见: 2.1 解析注解映射

结论

  1. 注解是一个接口
  2. 注解的使用JDK动态代理创建对象,最终调用 AnnotationInvocationHandler.invoke
  3. 必须要加入 @Retention(RetentionPolicy.RUNTIME),才能在运行时获取到值,否则会返回null

鸣谢

本文的反编译工具: http://www.benf.org/other/cfr/cfr_0_131.jar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值