Java反射
Java反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态的获取信息以及动态调用对象的方法 的功能称为java的反射机制。
首先你需要了解类加载的过程,这里我们简单提一下(加载-验证-准备-解析-初始化),反射是靠JVM和Class相关类实现的。
按照这个例子,我们调试下看看具体实现。
@Data public class Person { private String name; public static void main(String[] args) throws Exception { Person person = new Person(); person.setName("lewis"); for (int i = 0; i < 16; i++) { Method method = Person.class.getMethod("getName"); System.out.println(method.invoke(person)); } } }
@CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { // 检查方法是否为public if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); // 权限校验 checkAccess(caller, clazz, obj, modifiers); } } // MethodAccessor实现有两个版本,一个是Java实现的,另一个是JNI实现的 MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }
我们上面提到了 MethodAccessor 有两个实现,Java版本和JNI版本(就是java native),
Java实现的版本在初始化时需要较多时间,但长久来说性能较好;JNI版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。
为了尽可能地减少性能损耗,HotSpot JDK采用“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用JNI版,等反射调用次数超过阈值(15)时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版本。
ReflectionFactory.newMethodAccessor()生产MethodAccessor对象的逻辑,一开始(JNI版)会生产NativeMethodAccessorImpl和DelegatingMethodAccessorImpl两个对象。
public MethodAccessor newMethodAccessor(Method var1) { checkInitted(); if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) { return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers()); } else { NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1); DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2); var2.setParent(var3); return var3; } }
DelegatingMethodAccessorImpl的源码如下:
这是一个中间层,方便在JNI版本与Java版的MethodAccessor之间实现切换。
class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate; DelegatingMethodAccessorImpl(MethodAccessorImpl var1) { this.setDelegate(var1); } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { return this.delegate.invoke(var1, var2); } void setDelegate(MethodAccessorImpl var1) { this.delegate = var1; } }
来看看NativeMethodAccessorImpl实现,超过15次以后调用反射,就会通过我们上面提到的中间层 DelegatingMethodAccessorImpl 所引用的 MethodAccessor 都是java 版。
class NativeMethodAccessorImpl extends MethodAccessorImpl { private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method var1) { this.method = var1; } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { // 方法被调用时,程序调用计数器都会增加1,看看是否超过阈值 if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) { // 超过15次 则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类 // 并且改变通过中间层,后续DelegatingMethodAccessorImpl所引用的MethodAccessor改为Java版 MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers()); this.parent.setDelegate(var3); } // native版本,JNI方式调用 return invoke0(this.method, var1, var2); } void setParent(DelegatingMethodAccessorImpl var1) { this.parent = var1; } private static native Object invoke0(Method var0, Object var1, Object[] var2); }
在默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用。在调用超过 15 次之后,委派实现便会将委派对象切换至动态实现。这个动态的字节码是在Java运行过程中通过ASM自动生成的,它将直接使用 invoke 指令来调用目标方法。
继续查看代码,可以看到sun.reflect.MethodAccessorGenerator#generate
的实现是调用asm字节码增强工具来生成类,此过程较长,不在此列出。在该方法的最后,我们发现有这样一个操作sun.reflect.ClassDefiner#defineClass
,查看其源码
static Class<?> defineClass(String name, byte[] bytes, int off, int len, final ClassLoader parentClassLoader) { // 创建一个DelegatingClassLoader用来加载生成的类 ClassLoader newLoader = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return new DelegatingClassLoader(parentClassLoader); } }); return unsafe.defineClass(name, bytes, off, len, newLoader, null); }
参考: