java反射底层原理-粗略理解

反射

Posted by CHuiL on February 22, 2021

基本原理理解

我们编写完的class文件,会被编译为字节码,然后在程序运行时被类加载器加载到vm中,加载完成后,每个class都会有一个对应的Class对象,我们所有的这个类的对象都是由这个Class实例来创建的。 由于我们的Class实例对象是通过类加载class文件获得的,他能够获取到几乎有关于该class的所有信息,包括

  1. 版本号:主版本号和次版本号
  2. 常量池
  3. 访问标志:是类还是接口,public还是private
  4. 字段表集合
  5. 方法集合 等等;
    这些信息都会保存在Class对象实例中,所以我们可以利用Class来获取到有关该类的所有基本信息;

类型信息的获取

通过上面的介绍,因为类的所有信息都已经被加载到Class实例上了,自然可以通过该Class实例来获取;

方法的调用

假设有如下一个类A,利用反射调用foo方法。

public class A {  
    public void foo(String name) {  
        System.out.println("Hello, " + name);  
    }  
} 

public class TestClassLoad {  
    public static void main(String[] args) throws Exception {  
        Class<?> clz = Class.forName("A");  
        Object o = clz.newInstance();  
        Method m = clz.getMethod("foo", String.class);  
        for (int i = 0; i < 16; i++) {  
            m.invoke(o, Integer.toString(i));  
        }  
    }  
}  

Method类中invoke的源码

public final class Method extends Executable {
....
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }
....
}

可以看到是通过MothodAccessor来实现invoke的。 MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的,以下是MethodAccessorGenerator生成的java版MethodAccessor;

public class GeneratedMethodAccessor1 extends MethodAccessorImpl {      
    public GeneratedMethodAccessor1() {  
        super();  
    }  
      
    public Object invoke(Object obj, Object[] args)     
        throws IllegalArgumentException, InvocationTargetException {  
        // prepare the target and parameters  
        if (obj == null) throw new NullPointerException();  
        try {  
            A target = (A) obj;  
            if (args.length != 1) throw new IllegalArgumentException();  
            String arg0 = (String) args[0];  
        } catch (ClassCastException e) {  
            throw new IllegalArgumentException(e.toString());  
        } catch (NullPointerException e) {  
            throw new IllegalArgumentException(e.toString());  
        }  
        // make the invocation  
        try {  
            target.foo(arg0); 
        } catch (Throwable t) {  
            throw new InvocationTargetException(t);  
        }  
    }  
}  

字段设置

File字段的代码

class Field extends AccessibleObject implements Member {
    ...
    public void set(Object obj, Object value)
        throws IllegalArgumentException, IllegalAccessException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        getFieldAccessor(obj).set(obj, value);
    }
    ...
}

FieldAccessor是一个接口,提供set和get基本类型和object接口;有很多Unsafe**FieldAccessorImpl实现,如UnsafeObjectFieldAccessorImpl;

class UnsafeObjectFieldAccessorImpl extends UnsafeFieldAccessorImpl {
    ...
    public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
        this.ensureObj(var1);
        if (this.isFinal) {
            this.throwFinalFieldIllegalAccessException(var2);
        }

        if (var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) {
            this.throwSetIllegalArgumentException(var2);
        }

        unsafe.putObject(var1, this.fieldOffset, var2);
    }
    ...
}

他们最终都会调用到Unsafe.put**的接口;这些接口都是native的;通过调用putLong,putInt,putDouble,putChar,putObject等方法,可以直接修改内存数据,达到修改变量值的目的;

参考

Java反射原理简析
关于反射调用方法的一个log
java反射怎么实现的?
sun.misc.Unsafe的理解