工具类杂记

JDK17 反射获取私有变量的一种方案

根据StackOverflow老哥的帖子,自己参考Spring的ReflectionUtils封了两个函数。
原因大意为这个Issue说的,JDK加了一套过滤器机制,避免java.base模块下的类的(私有)变量和方法被外部使用和依赖。可是谁让我们是写Java的,主打一个喜欢反射。
另外,这个在启动后添加类似--add-opens功能的方案似乎对JDK17没有什么用。

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ReflectionUtils {
    /**
     * 缓存变量
     */
    private static final Map, Field[]> declaredFieldsCache = new ConcurrentHashMap<>();
    private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];
    private static final Method getDeclaredFields0 = initializeStaticFinalField();

    private static Method initializeStaticFinalField() {
        try {
            Method method = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
            method.setAccessible(true);
            return method;
        } catch (NoSuchMethodException e) {
            throw new RuntimeException();
        }
    }

    /**
     * Copied From org.springframework.util.ReflectionUtils.
     * 已知JDK 17不会向无名模块(Unnamed Module)开放反射权限
     * 将项目改造为模块似乎也没有任何作用,反而可能导致第三方框架的问题
     * 因此需结合JVM参数实现,例如给JVM启动增加 --add-opens java.base/java.lang=ALL-UNNAMED
     * 意为将java.base模块的java.lang包的(反射)权限开放给所有的无名包
     * 即使这样,直接用Class.getDeclaredFields也是无效的,需要曲线先获取Class的私有方法getDeclaredFields0或getDeclaredFields
     * 然后再在特定的java.lang包下的类的类对象调用Class类的私有方法
     * 这种方法的问题在于一次性必须获取全部字段,没有开放单个字段的获取方法,或者说JDK这一层没有考虑为其他模块开放该操作
     *
     * @param publicOnly 是否仅获取公共字段,可以直接看Class.getDeclaredFields0
     */
    public static Field[] getDeclaredFieldsAfterJDK12(Class clazz, boolean publicOnly) {
        if (clazz == null) {
            throw new NullPointerException();
        }
        Field[] result = declaredFieldsCache.get(clazz);
        if (result == null) {
            try {
                result = (Field[]) getDeclaredFields0.invoke(clazz, publicOnly);
                declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
            } catch (Throwable ex) {
                throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
                        "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
            }
        }
        return result;
    }


    /**
     * @param name       必须完全同名,name会因为有空格匹配不上,没有考虑做处理
     * @param publicOnly 同上
     */
    public static Field getDeclaredFieldAfterJDK12(Class clazz, String name, boolean publicOnly) {
        if (clazz == null || name == null || name.trim().isEmpty()) {
            throw new NullPointerException();
        }
        Field result = null;
        try {
            Field[] fields = getDeclaredFieldsAfterJDK12(clazz, publicOnly);
            for (Field field : fields) {
                if (name.equals(field.getName())) {
                    result = field;
                    break;
                }
            }
        } catch (Throwable ex) {
            throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
                    "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
        }
        return result;
    }
}

你可能感兴趣的:(工具类杂记)