java.lang.reflect(一)AnnotatedElement、GenericDeclaration与AccessibleObject

726 阅读10分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战


AnnotatedElement接口是java.lang.reflect包中的,通过这个接口可以延伸到整个反射包。AnnotatedElement表示目前正在此 VM 中运行的程序的一个已注释元素,相关子类结构如下:

  • AnnotatedElement
    • GenericDeclaration (java.lang.reflect)
      • Executable (java.lang.reflect)
        • Method (java.lang.reflect)
        • Constructor (java.lang.reflect)
      • Class (java.lang)
    • AccessibleObject (java.lang.reflect)
      • Executable (java.lang.reflect)
        • Method (java.lang.reflect)
        • Constructor (java.lang.reflect)
      • Field (java.lang.reflect)
    • Package (java.lang)
    • Parameter (java.lang.reflect)
    • TypeVariable (java.lang.reflect)
      • TypeVariableImpl (sun.reflect.generics.reflectiveObjects)
    • AnnotatedType (java.lang.reflect):子接口在AnnotatedTypeFactory工厂中生成实现类
      • AnnotatedParameterizedType (java.lang.reflect)
      • AnnotatedTypeVariable (java.lang.reflect)
      • AnnotatedArrayType (java.lang.reflect)
      • AnnotatedWildcardType (java.lang.reflect)
      • AnnotatedTypeBaseImpl in AnnotatedTypeFactory (sun.reflect.annotation)
    • Class (java.lang)

我从AnnotatedElement开始了解reflect包里的玄机。

一、java.lang.reflect.AnnotatedElement

1. AnnotatedElement

AnnotatedElement 接口用于表示VM 被注解的元素;该接口可以用反射读取注释,但是返回的所有注释都是不可变和可序列化的。另外,该接口的方法返回的数组可以被调用者修改,而不会影响返回给其他调用者的数组,即数组读取后是各自独立的。

相关术语

  • 直接声明(directly present):被注解元素上的单个注解
  • 间接声明(indirectly present):被注解元素上的重复注解
    • 重复注解:指类上存在的多个同类型注解
  • 声明(present):被注解元素及其父类的直接声明合集
  • 关联(associated):子类及父类所有的注解

AnnotatedElement 接口包含以下几个方法:

  • getAnnotation:从元素中获取指定注解(包括继承的注解);否则返回null
  • getAnnotations:返回此元素上存在的所有注解(包括继承与重复的);否则返回null
  • getDeclaredAnnotation:从元素中获取指定注解(不包括继承的注解);否则返回null
  • getDeclaredAnnotations:从元素中获取所有的注解(包括重复注解,不包括继承的);否则返回null
  • getAnnotationsByType 与 getDeclaredAnnotationsByType: 支持同一元素上存在多个相同类型的注解(repeatable annotation);如果任一方法的参数是可重复的注解类型,则可通过容器类(Map)查看重复注解
  • isAnnotationPresent:校验注解是否存在指定注解

相关源码:

public interface AnnotatedElement {

    /**
     * 如果此元素上存在指定注解 annotationClass,返回注解类型T;否则为 null
     */
    <T extends Annotation> T getAnnotation(Class<T> annotationClass);
    
    /**
     * 返回元素中存在的所有注解; 如果没有存在任何注解,返回值是长度为0。
     * 该方法的调用者可以随意修改返回的数组,而不会影响返回给其他调用者的数组。
     */
    Annotation[] getAnnotations();
    
    /**
      * 返回此元素关联的注解,如果不存在则返回长度为0的数组
      * 该方法会检测参数是否为可重复的注解类型; 该方法的调用者可以自由修改返回的数组; 它不会影响返回给其他调用者的数组
      */
    default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {

         T[] result = getDeclaredAnnotationsByType(annotationClass);
         
         // 判断当前类是否有注解
         if (result.length == 0 && // 当前类不存在直接或间接注解
             this instanceof Class && // 当前元素表示一个类
             AnnotationType.getInstance(annotationClass).isInherited()) { // 该注解可以继承
             
             // 满足以上几个条件,就可以查询所表示元素父类的注解
             Class<?> superClass = ((Class<?>) this).getSuperclass();
             if (superClass != null) {
                 // 确定注解是否与超类相关联
                 result = superClass.getAnnotationsByType(annotationClass);
             }
         }

         return result;
     }
    /**
     * 如果存在直接注解,则返回该注解;否则返回 null。此方法忽略继承的注解。
     */
    default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
        // 判断是否为空,为空抛出空指针异常
         Objects.requireNonNull(annotationClass);
         // 遍历所有直接存在的注释以寻找匹配的注解
         for (Annotation annotation : getDeclaredAnnotations()) {
             if (annotationClass.equals(annotation.annotationType())) {
                 // 在运行时进行动态转换比在编译时进行使代码更健壮
                     // Java.lang.Class类的cast()方法用于将指定的对象强制转换为此类的对象
                 return annotationClass.cast(annotation);
             }
         }
         return null;
     }
    
    /**
     * 获取所有注解。
     */
    Annotation[] getDeclaredAnnotations();
    
    /**
     * 如果此类注解直接或间接存在,则返回此元素的指定类型的注解;此方法忽略继承的注解。 如果此元素上没有直接或间接指定的注解,则返回值为长度为 0 的数组。
     * 此方法与getDeclaredAnnotation(Class)的区别在于该方法会检测其参数是否为可重复的注解类型,如果是会返回该类型的一个或多个注释
     */
    default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
        Objects.requireNonNull(annotationClass);
        return AnnotationSupport.
            getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
                                            collect(Collectors.toMap(Annotation::annotationType,
                                                                     Function.identity(),
                                                                     ((first,second) -> first),
                                                                     LinkedHashMap::new)),
                                            annotationClass);
    }
    /**
     * 如果此元素上存在指定类型的注释,则返回 true,否则返回 false。 此方法主要是为了方便访问标记注释而设计的 
     */
    default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }
}

如上所示,源码中调用了AnnotationSupportgetDirectlyAndIndirectlyPresent方法获取当前类的直接注解;该类顾名思义是为注解提供支持的工具类。


2. AnnotationSupport

AnnotationSupport类 位于sun.reflect.annotation包中,而sun包与Java平台的标准无关,与操作系统相关,不同操作系统的sun包实现也不一样。Oracle不建议开发者使用该包中的代码,因为该包的类实现与操作系统相关且可能在新的版本中被去除。

相关源码如下:

public final class AnnotationSupport {
    private static final JavaLangAccess LANG_ACCESS = sun.misc.SharedSecrets.getJavaLangAccess();

    /**
     * 查找并返回的所有匹配的annotations(直接+间接)
     * 返回的数组元素顺序取决于提供的Map的迭代顺序.
     * @param annotations 用于查询注解的Map
     * @param 注解类
     * @return annoClass的实例数组或空数组(如果没有找到)
     */
    public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent(
            Map<Class<? extends Annotation>, Annotation> annotations,
            Class<A> annoClass) {
        List<A> result = new ArrayList<A>();
        
        // 获取直接注解
        @SuppressWarnings("unchecked")
        A direct = (A) annotations.get(annoClass);
        if (direct != null)
            result.add(direct);
            
        // 获取间接注解
        A[] indirect = getIndirectlyPresent(annotations, annoClass);
        if (indirect != null && indirect.length != 0) {
            boolean indirectFirst = direct == null ||
                                    containerBeforeContainee(annotations, annoClass);
            //添加所有间接注解
            result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect));
        }

        @SuppressWarnings("unchecked")
        // 使用annoClass填充数组元素
        A[] arr = (A[]) Array.newInstance(annoClass, result.size());
        return result.toArray(arr);
    }

    /**
     * 查找与给定annoClass相关的间接注解,并返回存在于annotations中的所有匹配项.
     *
     * @param 用于按类型索引搜索的注解
     * @param 要搜索的注解类型
     * @return annoClass的实例数组或空数组
     */
    private static <A extends Annotation> A[] getIndirectlyPresent(
            Map<Class<? extends Annotation>, Annotation> annotations,
            Class<A> annoClass) {
            
        //Repeatable 类是元注解,用于赋予注解重复特性
        Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class);
        if (repeatable == null)
            return null;  // 表示没有间接注解

        Class<? extends Annotation> containerClass = repeatable.value();

        Annotation container = annotations.get(containerClass);
        if (container == null)
            return null; // 表示没有对应的注解容器

        // Unpack container
        A[] valueArray = getValueArray(container);
        checkTypes(valueArray, container, annoClass);

        return valueArray;
    }


    /**
     * 确定在给定映射的KEY中,conatiner 类是否在 containeree 类之前
     * @return  迭代 annotations.keySet() 时在容器类之前找到容器类,则为 true
     */
    private static <A extends Annotation> boolean containerBeforeContainee(
            Map<Class<? extends Annotation>, Annotation> annotations,
            Class<A> annoClass) {
        // 获取注解类的直接注解(可重复的)
        Class<? extends Annotation> containerClass =
                annoClass.getDeclaredAnnotation(Repeatable.class).value();

        for (Class<? extends Annotation> c : annotations.keySet()) {
            if (c == containerClass) return true;
            if (c == annoClass) return false;
        }

        // Neither containee nor container present
        return false;
    }


    /**
     * 查找并返回匹配给定类的所有相关注释.
     *
     * 返回的数组中元素的顺序取决于迭代MAP的顺序.
     *
     * @param declaredAnnotations 声明的注解
     * @param decl the 用于搜索注解的class
     * @param annoClass 搜索的注解类型
     *
     * @return 返回注解数组或空数组.
     */
    public static <A extends Annotation> A[] getAssociatedAnnotations(
            Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
            Class<?> decl,
            Class<A> annoClass) {
        Objects.requireNonNull(decl);

        // 获取直接或间接注解
        A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass);

        // Search inherited
        if(AnnotationType.getInstance(annoClass).isInherited()) {
            Class<?> superDecl = decl.getSuperclass();
            while (result.length == 0 && superDecl != null) {
                result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass);
                superDecl = superDecl.getSuperclass();
            }
        }

        return result;
    }


    /* Reflectively invoke the values-method of the given annotation
     * (container), cast it to an array of annotations and return the result.
     */
    private static <A extends Annotation> A[] getValueArray(Annotation container) {
        try {
            // According to JLS the container must have an array-valued value
            // method. Get the AnnotationType, get the "value" method and invoke
            // it to get the content.

            Class<? extends Annotation> containerClass = container.annotationType();
            AnnotationType annoType = AnnotationType.getInstance(containerClass);
            if (annoType == null)
                throw invalidContainerException(container, null);

            Method m = annoType.members().get("value");
            if (m == null)
                throw invalidContainerException(container, null);

            m.setAccessible(true);

            // This will erase to (Annotation[]) but we do a runtime cast on the
            // return-value in the method that call this method.
            @SuppressWarnings("unchecked")
            A[] values = (A[]) m.invoke(container);

            return values;

        } catch (IllegalAccessException    | // couldn't loosen security
                 IllegalArgumentException  | // parameters doesn't match
                 InvocationTargetException | // the value method threw an exception
                 ClassCastException e) {

            throw invalidContainerException(container, e);

        }
    }


    // 表示注解格式错误
    private static AnnotationFormatError invalidContainerException(Annotation anno,
                                                                   Throwable cause) {
        return new AnnotationFormatError(
                anno + " is an invalid container for repeating annotations",
                cause);
    }


    /* 检查注解数组annotations 中所有元素是否是annoClass类型
     */
    private static <A extends Annotation> void checkTypes(A[] annotations,
                                                          Annotation container,
                                                          Class<A> annoClass) {
        for (A a : annotations) {
            if (!annoClass.isInstance(a)) {
                throw new AnnotationFormatError(
                        String.format("%s is an invalid container for " +
                                      "repeating annotations of type: %s",
                                      container, annoClass));
            }
        }
    }
}

二、GenericDeclaration 及其实现类

GenericDeclaration接口与泛型相关,只有实现了该接口的类才可以使用泛型<E>

1. GenericDeclaration

GenericDeclaration 接口与泛型相关,声明了类型变量中所有实体的通用接口;而类型变量指的是 Class,Constructor,Method等类。相关类结构如下:

  • AnnotatedElement
    • GenericDeclaration (java.lang.reflect)
      • Executable (java.lang.reflect)
        • Method (java.lang.reflect)
        • Constructor (java.lang.reflect)
      • Class (java.lang)

GenericDeclaration接口只有一个方法声明 public TypeVariable<?>[] getTypeParameters(),用于按声明顺序返回一个TypeVariable对象数组,返回的其实是类上定义的泛型。

例如

public class Test<T> {
    public static void main(String[] args) {
        TypeVariable<Class<Test>>[] typeParameters = Test.class.getTypeParameters();
        for (TypeVariable typeVariable: typeParameters) {
            System.out.println(typeVariable.getName());
        }
    }
}

image.png

在DEBUG时,可以看到getTypeParameters返回的数组包含一个名为T的元素,这个其实是在类Test中提取的泛型声明。

2. AccessibleObject

AccessibleObject 类用于可访问性控制,是 Field、Method 和 Constructor 对象的基类。默认情况下,Java 语言会对代码的访问进行控制检查,而设置标记可以在使用时抑制检查。

当字段、方法或构造函数用于设置或获取字段、调用方法或创建和初始化类的新实例时,将执行访问检查——publicdefault(包)、protectedprivate, 在反射对象中设置accessible标志允许具有足够权限的复杂应用程序(如 Java 对象序列化或其他持久性机制)使用通常情况下被禁止的方式操作对象(默认情况下,反射对象不可访问)。

相关字段:

// Permission 对象,用于检查客户端是否有足够的权限来阻止 Java 语言访问控制检查。
static final private java.security.Permission ACCESS_PERMISSION =
    new ReflectPermission("suppressAccessChecks");
// 指示此对象是否覆盖语言级别的访问检查
boolean override;
// 子类用于创建字段、方法和构造函数访问器的反射工厂
static final ReflectionFactory reflectionFactory =
    AccessController.doPrivileged(
        new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
// 用于共享访问检查逻辑,记录最近一次的成功记录,加速校验
volatile Object securityCheckCache;

相关方法:

  • 与注解相关,带getDeclaredXXX用于获取直接注解;这几个方法是实现了AnnotatedElement接口中的方法
    • getAnnotation
    • getAnnotations
    • getAnnotationsByType
    • getDeclaredAnnotation
    • getDeclaredAnnotations
    • getDeclaredAnnotationsByType
    • isAnnotationPresent:查看是否存在指定类型的注解
  • 与访问权限相关
    • isAccessible:获取此对象的accessible标志的值,表示是否有权限访问
    • setAccessible、setAccessible、setAccessible0 用于允许访问
    • checkAccess 用于检测反射时调用者是否有权限调用方法,当检测不通过时调用slowCheckMemberAccess方法
    • slowCheckMemberAccess

相关实例:

public class Test<T> { 
    @Deprecated
    private String msg = "msg";
    public static void main(String[] args) throws NoSuchFieldException {

//        获取非public的字段
        for (Field field: Test.class.getDeclaredFields()) {
            System.out.println(field.getGenericType().getTypeName());
            System.out.println(field.isAccessible());
            System.out.println(field.isAnnotationPresent(Deprecated.class));
            System.out.println(field);
        }
     }
}

setAccessible

存在三个设置访问权限的方法,以其中一个源码为例:

public static void setAccessible(AccessibleObject[] array, boolean flag)
    throws SecurityException {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
    for (int i = 0; i < array.length; i++) {
        setAccessible0(array[i], flag);
    }
}
  • 首先,会先从System获取SecurityManager(安全管理器),通过这个类判断客户端是否有足够的权限阻止检查;
  • 检验成功时,调用setAccessible0方法设置标志;
  • 校验失败,则抛出异常AccessControlException.

checkAccess

checkAccess方法的判断逻辑:

  • 若调用者和被调用者的类一致,那么验证通过;
  • 如果以下三个条件成立,则判断securityCheckCache中是否存在[caller,targetClass]的缓存或是caller==cache,存在则验证通过
    • 调用的是set方法(obj不为空)
    • 字段、函数、构造器是受保护的
    • set方法设置的类型不是目被调用者
  • 否则,调用slowCheckMemberAccess方法进入缓慢校验的路径。
void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers)
    throws IllegalAccessException
{
    if (caller == clazz) {  // quick check
        return;             // ACCESS IS OK
    }
    Object cache = securityCheckCache;  // read volatile
    Class<?> targetClass = clazz;
    if (obj != null
        && Modifier.isProtected(modifiers)
        && ((targetClass = obj.getClass()) != clazz)) {
        // Must match a 2-list of { caller, targetClass }.
        if (cache instanceof Class[]) {
            Class<?>[] cache2 = (Class<?>[]) cache;
            if (cache2[1] == targetClass &&
                cache2[0] == caller) {
                return;     // ACCESS IS OK
            }
            // (Test cache[1] first since range check for [1]
            // subsumes range check for [0].)
        }
    } else if (cache == caller) {
        // Non-protected case (or obj.class == this.clazz).
        return;             // ACCESS IS OK
    }

    // If no return, fall through to the slow path.
    slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
}

slowCheckMemberAccess

slowCheckMemberAccess方法通过缓慢遍历校验是否拥有调用权限,由以下源码可知该方法执行时会调用ensureMemberAccess方法进行权限判定,判定失败抛出异常IllegalAccessException,表示非法的访问异常;判定成功则更新缓存,便于下次快速校验。

void slowCheckMemberAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers,
                           Class<?> targetClass)
    throws IllegalAccessException
{
    Reflection.ensureMemberAccess(caller, clazz, obj, modifiers);

    // 成功则更新缓存
    Object cache = ((targetClass == clazz)
                    ? caller
                    : new Class<?>[] { caller, targetClass });

    // 缓存,加快下次校验
    securityCheckCache = cache;         // 写入 volatile变量
}


public static void ensureMemberAccess(Class<?> var0, Class<?> var1, Object var2, int var3) throws IllegalAccessException {
    if (var0 != null && var1 != null) {
        if (!verifyMemberAccess(var0, var1, var2, var3)) {
            throw new IllegalAccessException("Class " + var0.getName() + " can not access a member of class " + var1.getName() + " with modifiers "" + Modifier.toString(var3) + """);
        }
    } else {
        throw new InternalError();
    }
}