反射及其原理

目录

image.png

例子

  • 定义一个User类
public class User {
    private String name;
    private Integer age;
    // 省略get set
}
反射设置属性
public class Test {


    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("User");
        testReflection(clazz);
    }

    /**
     * 如果遇到多个类都有name属性并需要提供统一处理这时候就很好用
     * @param clazz
     */
    public static void testReflection(Class clazz) {
        try {
            if (clazz == null) {
                System.out.println("空 clazz");
                return;
            }
            //获得类的实例
            Object obj = clazz.newInstance();
            //获得 User 类中的指定属性对应的Field对象(每个属性对应一个Field对象)
            Field field = clazz.getDeclaredField("name");

            //取消属性的访问权限控制,即使private 属性也可以进行访问
            field.setAccessible(true);
            //调用 getter 方法获取属性值
            System.out.println(field.get(obj));
            //调用setter 方法给属性赋值
            field.set(obj, "scott");
            //调用 getter 方法获取对应属性修改后的值
            System.out.println(field.get(obj));
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("异常");
        }

    }


}
反射执行方法
public static void main(String[] args) throws Exception {
    Class clazz = User.class;
    Object obj = clazz.newInstance();


    //调用该对象的 setName方法
    Method method = clazz.getMethod("setName", new Class[]{String.class});
    Object result = method.invoke(obj, new Object[]{"scott"});       //  obj.setName("scott");
    System.out.println("返回值为:" + result);

    //调用对象的getName()方法
    Method method1 = clazz.getMethod("getName", new Class[]{});
    Object obj1 = method1.invoke(obj, new Object[]{});
    System.out.println("返回值为:" + obj1);

}

原理

  • 代码示例
Class<?> clazz = Class.forName("TestOOM");
TestOOM testOOM = (TestOOM) clazz1.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("test");
method.invoke(testOOM1);
  • 先看下method的invoke方法,Method实例对象维护了一个root引用。当调用Method.copy()进行方法拷贝时,root指向了被拷贝的对象,通过维护root引用意图使相同的方法始终保持只有一个methodAccessor实例。ma.invoke会先通过DelegatingMethodAccessorImpl.invoke,再到NativeMethodAccessorImpl.invoke,NativeMethodAccessorImpl最终调用native方法完成invoke()任务。
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException{
    // 检查override,如果override为true,跳过检查
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    // private volatile MethodAccessor methodAccessor;
    MethodAccessor ma = methodAccessor;          
    if (ma == null) {
        // ma不存在则去获取
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

private MethodAccessor acquireMethodAccessor() {
    // First check to see if one has been created yet, and take it if so
    MethodAccessor tmp = null;
    if (root != null) tmp = root.getMethodAccessor();
    if (tmp != null) {
        methodAccessor = tmp;
    } else {
        // Otherwise fabricate one and propagate it up to the root
        tmp = reflectionFactory.newMethodAccessor(this);
        setMethodAccessor(tmp);
    }

    return tmp;
}

public MethodAccessor newMethodAccessor(Method method) {
    checkInitted();
    // 启动参数-Dsun.reflect.noInflation
    if (noInflation) {
        //省略部分部分
    } else {
        NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
        // 委托模式
        DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
        acc.setParent(res);
        return res;
    }
}

void setMethodAccessor(MethodAccessor accessor) {
    methodAccessor = accessor;
    // Propagate up
    if (root != null) {
        root.setMethodAccessor(accessor);
    }
}

// 每次获取Method都是new的,类似cow思想
Method copy() {
    Method res = new Method(clazz, name, parameterTypes, returnType,
                            exceptionTypes, modifiers, slot, signature,
                            annotations, parameterAnnotations, annotationDefault);
    res.root = this;
    res.methodAccessor = methodAccessor;
    return res;
}
  • NativeMethodAccessorImpl最终调用native方法完成invoke()任务。当numInvocations数量大于配置项sun.reflect.inflationThreshold(m默认15)即类膨胀阈值时, 使用MethodAccessorGenerator创建一个代理类对象,并且将被委托的NativeMethodAccessorImpl的parent。总体来说,当调用invoke()时,按照默认配置,Method首先创建一个DelegatingMethodAccessorImpl对象,并设置一个被委托的NativeMethodAccessorImpl对象,那么method.invoke()就被转换成DelegatingMethodAccessorImpl.invoke(),然后又被委托给NativeMethodAccessorImp.invoke()实现。当NativeMethodAccessorImp.invoke()调用次数超过一定热度时(默认15次),被委托方又被转换成代理类来实现。图中DelegatingMethodAccessorImpl用到代码模式,可能代理1,也可能代理2,代理1到一定条件时,代理1通过this.parent.setDelegate更换DelegatingMethodAccessorImpl代理的类。


    image.png
public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
    if (++numInvocations > ReflectionFactory.inflationThreshold()) {
        MethodAccessorImpl acc = (MethodAccessorImpl)
            new MethodAccessorGenerator().
                generateMethod(method.getDeclaringClass(),
                               method.getName(),
                               method.getParameterTypes(),
                               method.getReturnType(),
                               method.getExceptionTypes(),
                               method.getModifiers());
        parent.setDelegate(acc);
    }

    return invoke0(method, obj, args);
}

// JNI调用 
private static native Object invoke0(Method m, Object obj, Object[] args);
  • 接下来看看getDeclaredMethod, 这里特别注意privateGetDeclaredMethods,其中涉及软引用,如果getDeclaredMethod用到软引用缓存的,上面的MethodAccessor就是同一个,即使并发导致生成多个GeneratedMethodAccessorXXX(GeneratedMethodAccessorXXX的类加载器其实是一个DelegatingClassLoader类加载器, 这样更方便卸载)也生成的不大;但是如果非软引用而是重新生成的,那就好大量有DelegatingClassLoader加载,GeneratedMethodAccessorXXX大量加载卸载的问题。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
    checkInitted();
    Method[] res;
    // 软引用
    ReflectionData<T> rd = reflectionData();
    if (rd != null) {
        res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
        if (res != null) return res;
    }
    // No cached value available; request value from VM
    res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
    if (rd != null) {
        if (publicOnly) {
            rd.declaredPublicMethods = res;
        } else {
            rd.declaredMethods = res;
        }
    }
    return res;
}
  • 总结: Method.invoke的逻辑,相关的method因此被SoftReference引用,因此很容易被回收,一旦被回收,那就创建一个新的Method对象,再调用其invoke方法,在调用到一定次数(15次)之后,就构建一个新的字节码类,伴随着GC的进行,同一个方法的字节码类不断构建,直到将Perm充满触发一次Full GC才得以释放

Class.forName 和 ClassLoader区别

  • 在java中Class.forName()和ClassLoader都可以对类进行加载。ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的。
  • Spring框架中的IOC的实现就是使用的ClassLoader,JDBC时通常是使用Class.forName()方法来加载数据库连接驱动。这是因为在JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己。

设计模式学习

  • DelegatingMethodAccessorImpl就是用到委托模式,setDelegate, parent等关键字还可以改变委托的对象。委托模式是一种更高级的代理模式,委托模式可以解决一种方案的多种实现之间自由切换,而代理模式只能根据传入的被代理对象来实现功能。
  • 结构型之代理模式
  • 装饰器模式

反射和序列化破坏单例解决方案

优化

  • 可以把查找到的缓存起来。反射调用时本身就有用弱引用进行缓存。

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。