@toc
反射的概念
反射: Refelection,反射是Java的特色之一,容许运行中的Java程序获取本身信息,并能够操作类或者对象的外部属性
- 通过反射,能够在运行时取得程序或者程序中的每一个类型的成员活成成员的信息
- 程序中的对象个别都是在编译时就确定下来,Java反射机制能够动静地创建对象并且调用相干属性,这些对象的类型在编译时是未知的
- 也就是说 ,能够通过反射机制间接创建对象,即便这个对象类型在编译时是未知的
Java反射提供下列性能:
- 在运行时判断任意一个对象所属的类
- 在运行时结构任意一个类的对象
- 在运行时判断任意一个类所具备的成员变量和办法,能够通过反射调用private办法
在运行时调用任意一个对象的办法
反射的原理
- 反射的外围: JVM在运行时才动静加载类或者调用办法以及拜访属性,不须要当时(比方编译时)晓得运行对象是什么
类的加载:
- Java反射机制是围绕Class类开展的
首先要理解类的加载机制:
JVM应用ClassLoader将字节码文件,即 class文件加载到办法区内存中
Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.mypackage.MyClass");
ClassLoader类依据类的齐全限定名加载类并返回一个Class对象
ReflectionData:
- 为了进步反射的性能,必须要提供缓存
- class类外部应用一个useCaches动态变量来标记是否应用缓存
- 这个值能够通过内部的sun.reflect.noCaches配置是否禁用缓存
- class类外部提供了一个ReflectionData外部类用来寄存反射数据的缓存,并申明了一个reflectionData域
因为稍后进行按需提早加载并缓存,所以这个域并没有指向一个实例化的ReflectionData对象
// 标记是否应用缓存,能够通过内部的sun.reflect.noCaches配置是否禁用缓存private static boolean useCaches = true;static class ReflectionData<T> {volatile Field[] declaredFields;volatile Field[] publicFields;volatile Method[] declaredMethods;volatile Method[] publicMethods;volatile Constructor<T>[] declaredConstructors;volatile Constructors<T>[] publicConstructors;volatile Field[] declaredPublicFields;volatile Method[] declaredPublicMethods;final int redefinedCount;ReflectionData(int redefinedCount) { this.redefinedCount = redefinedCount;}}// 这个是SoftReference,在内存资源缓和的时候可能会被回收// volatile保障多线程环境下读写的正确性 private volatile transient SoftReference<RefelectionData<T>> reflectionData;// 这个次要用于和ReflectionData中的redefinedCount进行比拟// 如果两个值不相等,阐明ReflectionData缓存的数据曾经过期了private volatile transient classRedefinedCount = 0;
反射的主要用途
反射最重要的用处就是开发各种通用框架
- 很多框架都是配置化的,通过XML文件配置Bean
- 为了保障框架的通用性,须要依据配置文件加载不同的对象或者类,调用不同的办法
- 要使用反射,运行时动静加载须要加载的对象
示例:
在使用Struts 2框架的开发中会在struts.xml中配置Action:
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action>
- 配置文件与Action建设映射关系
- 当View层发出请求时,申请会被StrutsPrepareAndExecuteFilter拦挡
StrutsPrepareAndExecuteFilter会动静地创立Action实例
- 申请login.action
- StrutsPrepareAndExecuteFilter会解析struts.xml文件
- 检索action中name为login的Action
- 依据class属性创立SimpleLoginAction实例
- 应用invoke办法调用execute办法
反射是各种容器实现的外围
反射的使用
- 反射相干的类在StrutsPrepareAndExecuteFilter包
反射能够用于:
- 判断对象所属的类
- 取得class对象
- 结构任意一个对象
- 调用一个对象
九大预约义的Class对象:
- 根本的Java类型: boolean, byte, char, short, int, long, float, double
- 关键字void通过class属性的也示意为Class对象
包装类或者void类的动态TYPE字段:
- Integer.TYPE == int.class
- Integer.class == int.class
数组类型的Class实例对象:
- Class<String[]> clz = String[].class;
数组的Class对象如何比拟是否相等:
- 数组的维数
- 数组的类型
Class类中的isArray(),用来判断是否示意一个数组类型
取得Class对象
应用Class类的forName静态方法:
public static Class<?> forName(String className);/* 在JDBC中应用这个办法加载数据库驱动 */Class.forName(driver);
间接获取一个对象的class:
Class<?> klass=int.class;Class<?> classInt=Integer.TYPE;
调用对象的getClass()办法:
StringBuilder str=new StringBuilder("A");Class<?> klass=str.getClass();
判断是否是某个类的实例
- 一般来说,应用instanceof关键字判断是否为某个类的实例
在反射中,能够应用Class对象的isInstance() 办法来判断是否为某个类的实例,这是一个native办法
public native boolean isInstance(Object obj);
创立实例
通过反射生成对象的实例次要有两种形式:
应用Class对象的newInstance()办法来创立Class对象对应类的实例:
Class<?> c = String.class;Object str = c.newInstance();
先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()办法来创立实例: 能够用指定的结构器结构类的实例
/* 获取String所对应的Class对象 */Class<?> c=String.class;/* 获取String类带一个String参数的结构器 */Constructor constructor=c.getConstructor(String.class);/* 依据结构器创立实例 */Object obj=constructor.newInstance("abc");System.out.println(obj);
获取办法
获取Class对象的办法汇合,次要有三种办法:
getDeclaredMethods(): 返回类或接口申明的所有办法:
- 包含公共,爱护,默认(包)拜访和公有办法
不包含继承的办法
public Method[] getDeclaredMethods() throws SecurityException {}
getMethods(): 返回某个类所有的public办法
包含继承类的public办法
public Method[] getMethods() throws SecurityException {}
getMethod(): 返回一个特定的办法
- 第一个参数 :办法名称
前面的参数 :办法的参数对应Class的对象
public Method getMethod(String name,Class<?>... parameterType) {}
获取办法示例:
public class MethodTest { public static void methodTest() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<?> c = methodClass.class; Object object = c.newInstance(); Method[] methods = c.getMethods(); Method[] declaredMethods = c.getDeclaredMethods(); // 获取methodClass类中的add办法 Method method = c.getMethod("add", int.class, int.class); // getMethods()办法获取的所有办法 System.out.println("getMethods获取的办法:"); for (Method m:methods) System.out.println(m); // getDeclaredMethods()办法获取的所有办法 System.out.println("getDeclaredMethods获取的办法:"); for (Method m:declaredMethods) System.out.println(m); }}class methodClass { public final int n = 3; public int add(int a, int b) { return a + b; } public int sub(int a, int b) { return a * b; }}
程序运行后果:
getMethods获取的办法:public int org.ScZyhSoft.common.methodClass.add(int,int)public int org.ScZyhSoft.common.methodClass.sub(int,int)public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionpublic final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionpublic boolean java.lang.Object.equals(java.lang.Object)public java.lang.String java.lang.Object.toString()public native int java.lang.Object.hashCode()public final native java.lang.Class java.lang.Object.getClass()public final native void java.lang.Object.notify()public final native void java.lang.Object.notifyAll()getDeclaredMethods获取的办法:public int org.ScZyhSoft.common.methodClass.add(int,int)public int org.ScZyhSoft.common.methodClass.sub(int,int)
通过getMethods() 获取的办法能够获取到父类的办法
获取结构器信息
- 通过Class类的getConstructor办法失去Constructor类的一个实例
Constructor类中newInstance办法能够创立一个对象的实例:
public T newInstance(Objec ... initargs)
newInstance办法能够依据传入的参数来调用对应的Constructor创建对象的实例
获取类的成员变量信息
- getFileds: 获取私有的成员变量
getDeclaredFields: 获取所有已申明的成员变量,然而不能失去父类的成员变量
调用办法
从类中获取一个办法后,能够应用invoke() 来调用这个办法
public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
示例:
public class InvokeTest { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<?> klass = method.class; // 创立methodClass的实例 Object obj = klass.newInstance(); // 获取methodClass的add办法 Method method = klass.getMethod("add", int.class, int.class); // 调用method对应的办法,实现add(1,4) Object result = method.invoke(obj, 1, 4); System.out.println(result); }}class methodClass { public final int n = 3; public int add(int a, int b) { return a + b; } public int sub(int a,int b) { return a * b; }}
利用反射创立数组
- 数组是Java中一种非凡的数据类型,能够赋值给一个Object Reference
利用反射创立数组的示例:
public static void ArrayTest() throws ClassNotFoundException { Class<?> cls = class.forName("java.lang.String"); Object array = Array.newInstance(cls, 25); // 在数组中增加数据 Array.set(array, 0, "C"); Array.set(array, 1, "Java"); Array.set(array, 2, "Python"); Array.set(array, 3, "Scala"); Array.set(array, 4, "Docker"); // 获取数据中的某一项内容 System.out.println(Array.get(array, 3));}
Array类是java.lang.reflect.Array类,通过Array.newInstance() 创立数组对象:
public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length);}
newArray办法是一个native办法,具体实现在HotSpot JVM中,源码如下:
private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException;------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
newArray源码目录: openjdk\hotspot\src\share\vm\runtime\reflection.cpp
arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {
if (element_mirror == NULL) {
THROW_0(vmSymbols::java_lang_NullPointerException());
}
if (length < 0) {
THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
}
if (java_lang_Class::is_primitive(element_mirror)) {
Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
} else {
Klass* k = java_lang_Class::as_Klass(element_mirror);
if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {THROW_0(vmSymbols::java_lang_IllegalArgumentException());
}
return oopFactory::new_objArray(k, length, THREAD);
}
}Array类的set和get办法都是native办法,具体实现在HotSpot JVM中,对应关系如下:
- set: Reflection::array_set
get: Reflection::array_get
invoke办法
在Java中很多办法都会调用invoke办法,很多异样的抛出多会定位到invoke办法:
java.lang.NullPointerExceptionat ......at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)
invoke执行过程
invoke办法用来在运行时动静地调用某个实例的办法,实现如下:
@CallSensitivepublic Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Refelection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args);}
权限查看
AccessibleObject类是Field,Method和Constructor对象的基类:
- 提供将反射的对象标记为在应用时勾销默认Java语言访问控制查看的能力
invoke办法会首先查看AccessibleObject的override属性的值:
override默认值为false:
- 示意须要权限调用规定,调用办法时须要查看权限
- 也能够应用setAccessible() 设置为true
override如果值为true:
- 示意疏忽权限规定,调用办法时无需查看权限
- 也就是说,能够调用任意private办法,违反了封装
- 如果override属性为默认值false,则进行进一步的权限查看:
首先用Reflection.quickCheckMemberAccess(clazz, modifiers) 办法查看办法是否为public
1.1 如果是public办法的话,就跳过本步
1.2 如果不是public办法的话,就用Reflection.getCallerClass()办法获取调用这个办法的Class对象,这是一个native办法@CallerSensitive public static native Class<?> getCallerClass();-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在OpenJDK中能够找到getCallerClass办法的JNI入口-Reflection.c
JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__
(JNIEnv *env, jclass unused)
{
return JVM_GetCallerClass(env, JVM_CALLER_DEPTH);
}JVM_GetCallerClass的源码位于jvm.cpp中
VM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth))
JVMWrapper("JVM_GetCallerClass");
// Pre-JDK 8 and early builds of JDK 8 don't have a CallerSensitive annotation; or
// sun.reflect.Reflection.getCallerClass with a depth parameter is provided
// temporarily for existing code to use until a replacement API is defined.
if (SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth != JVM_CALLER_DEPTH) {
Klass* k = thread->security_get_caller_class(depth);
return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror());
}
// Getting the class of the caller frame.
//
// The call stack at this point looks something like this:
//
// [0] [ @CallerSensitive public sun.reflect.Reflection.getCallerClass ]
// [1] [ @CallerSensitive API.method ]
// [.] [ (skipped intermediate frames) ]
// [n] [ caller ]
vframeStream vfst(thread);
// Cf. LibraryCallKit::inline_native_Reflection_getCallerClass
for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) {
Method* m = vfst.method();
assert(m != NULL, "sanity");
switch (n) {
case 0:// This must only be called from Reflection.getCallerClassif (m->intrinsic_id() != vmIntrinsics::_getCallerClass) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass must only be called from Reflection.getCallerClass");}// fall-through
case 1:
// Frame 0 and 1 must be caller sensitive.if (!m->caller_sensitive()) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d", n));}break;
default:
if (!m->is_ignored_by_security_stack_walk()) { // We have reached the desired frame; return the holder class. return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror());}break;
}
}
return NULL;
JVM_END获取Class对象caller后应用checkAccess办法进行一次疾速的权限校验 ,checkAccess办法实现如下:
volatile Object securityCheckCache; void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers) throws IllegalAccessException { if(caller == clazz){ // 疾速校验 return; // 权限通过校验 } Object cache = securityCheckCache; // 读取volatile Class<?> targetClass = clazz; if (obj != null && Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) != clazz)) { // 必须匹配caller,targetClass中的一个 if (cache instanceof Class[]) { Class<?>[] cache2 = (Class<?>[]) cache; if (cache2[1] == targetClass && cache[0] == caller) { return; // 校验通过 } } } else if (cache == caller) { return; // 校验通过 } slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass); }
首先先执行一次疾速校验,一旦Class正确则权限查看通过;如果未通过,则创立一个缓存,两头再进行查看
如果下面所有的权限查看都未通过,将会执行更具体的查看:
void slowCheckMemberAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers, Class<?> targetClass) throws IllegalAccessException { Refelection.ensureMemberAccess(caller, clazz, obj, modifiers); // 如果胜利,就更新缓存 Object cache = ((targetClass == clazz) ? caller : new Class<?>[] {caller, targetClass}); securityCheckCache = cache;}
用Reflection.ensureMemberAccess办法持续查看权限.若查看通过就更新缓存,这样下一次同一个类调用同一个办法时就不必执行权限查看了,这是一种简略的缓存机制
因为JMM的happens-before规定可能保障缓存初始化可能在写缓存之间产生,因而两个cache不须要申明为volatile查看权限的工作到此结束.如果没有通过查看就会抛出异样,如果通过查看就会到下一步
调用MethodAccessor的invoke办法
- Method.invoke() 不是本身实现反射调用逻辑,而是通过sun.refelect.MethodAccessor来解决
Method对象的根本形成:
- 每个Java办法有且只有一个Method对象作为root, 相当于根对象,对用户不可见
- 当创立Method对象时,代码中取得的Method对象相当于其正本或者援用
- root对象持有一个MethodAccessor对象,所有获取到的Method对象都共享这一个MethodAccessor对象
- 必须保障MethodAccessor在内存中的可见性
root对象及其申明:
private volatile MethodAccessor methodAccessor;/** * For sharing of MethodAccessors. This branching structure is * currently only two levels deep (i.e., one root Method and * potentially many Method objects pointing to it.) * * If this branching structure would ever contain cycles, deadlocks can * occur in annotation code. */private Method root;
MethodAccessor:
/** * This interface provides the declaration for * java.lang.reflect.Method.invoke(). Each Method object is * configured with a (possibly dynamically-generated) class which * implements this interface */ public interface MethodAccessor { // Matches specification in {@link java.lang.reflect.Method} public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException; }
MethodAccessor是一个接口,定义了invoke() 办法,通过Usage能够看出MethodAccessor的具体实现类:
- sun.reflect.DelegatingMethodAccessorImpl
- sun.reflect.MethodAccessorImpl
- sun.reflect.NativeMethodAccessorImpl
- 第一次调用Java办法对应的Method对象的invoke()办法之前,实现调用逻辑的MethodAccess对象还没有创立
第一次调用时,才开始创立MethodAccessor并更新为root, 而后调用MethodAccessor.invoke() 实现反射调用
/** * NOTE that there is no synchronization used here. * It is correct(though not efficient) to generate more than one MethodAccessor for a given Method. * However, avoiding synchronization will probably make the implementation more scalable. */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 { tmp = reflectionFactory.newMethodAccessor(this); setMethodAccessor(tmp); } return tmp;}
methodAccessor实例由reflectionFactory对象操控生成 ,reflectionFactory是在AccessibleObject中申明的:
/** * Reflection factory used by subclasses for creating field, * method, and constructor accessors. Note that this is called very early in the bootstrapping process. */static final ReflectionFactory reflectionFactory = AccessController.doPrivileged( new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
sun.reflect.ReflectionFactory办法:
public class ReflectionFactory { private static boolean initted = false; private static Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess"); private static ReflectionFactory soleInstance = new ReflectionFactory(); // Provides access to package-private mechanisms in java.lang.reflect private static volatile LangReflectAccess langReflectAccess; /** * "Inflation" mechanism. Loading bytecodes to implement Method.invoke() and Constructor. * newInstance() currently costs 3-4x more than an invocation via native code for the first invocation (though subsequent invocations have been benchmarked to be over 20x faster) * Unfortunately this cost increases startup time for certain applications that use reflection intensively (but only once per class) to bootstrap themselves * To avoid this penalty we reuse the existing JVM entry points for the first few invocations of Methods and Constructors and then switch to the bytecode-based implementations */ // Package-private to be accessible to NativeMethodAccessorImpl and NativeConstructorAccessorImpl private static noInflation = false; private static int inflationThreshold = 15; // 生成MethodAccessor public MethodAccessor newMethodAccessor(Method method) { checkInitted(); if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); } else { NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method); DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc); acc.setParent(res); return res; } } /** * We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect * Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up */ private static void checkInitted() { if (initted) return; AccessController.doPrivileged( new PrivilegedAction<Void>() { public Void run() { /** * Tests to ensure the system properties table is fully initialized * This is needed because reflection code is called very early in the initialization process (before command-line arguments have been parsed and therefore these user-settable properties installed * We assume that if System.out is non-null then the System class has been fully initialized and that the bulk of the startup code has been run */ if (System.out == null) { // java.lang.System not yet fully initialized return null; } String val = System.getProperty("sun.reflect.noInflation"); if (val != null && val.equals("true")) { noInflation = true; } val = System.getProperty("sun.reflect.inflationThreshold"); if (val != null) { try { inflationThreshold = Integer.parseInt(val); } catch (NumberFormatException e) { throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e); } } initted = true; return null; } }); }}
理论的MethodAccessor实现有两个版本,一个是Java版本,一个是native版本,两者各有特点:
- 首次启动时Method.invoke() 和Constructor.newInstance() 办法采纳native办法要比Java办法快3-4倍
- 启动后native办法又要耗费额定的性能而慢于Java办法
- Java实现的版本在初始化时须要较多工夫,但短暂来说性能较好
这是HotSpot的优化形式带来的性能个性:
- 逾越native边界会对优化有妨碍作用
为了尽可能地缩小性能损耗,HotSpot JDK采纳inflation形式:
- Java办法在被反射调用时,结尾若干次应用native版
- 等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke() 办法的字节码
- 当前对该Java办法的反射调用就会应用Java版本
ReflectionFactory.newMethodAccessor() 生成MethodAccessor对象的逻辑:
- native版开始会会生成NativeMethodAccessorImpl和DelegatingMethodAccessorImpl两个对象
DelegatingMethodAccessorImpl:
/* Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time */class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate; DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) { setDelegate(delegate); } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { return delegate.invoke(obj, args); } void setDelegate(MethodAccessorImpl delegate) { this.delegate = delegate; }}
DelegatingMethodAccessorImpl对象是一个中间层,不便在native版与Java版的MethodAccessor之间进行切换
native版MethodAccessor的Java方面的申明: sun.reflect.NativeMethodAccessorImpl
/* Used only for the first few invocations of a Method; afterward,switches to bytecode-based implementation */class NativeMethodAccessorImpl extends MethodAccessorImpl { private Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method method) { this.method = method; } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { /* We can't inflate methods belonging to vm-anonymous classes because that kind of class can't be referred to by name, hence can't be found from the generated bytecode */ if (++numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { 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); } void setParent(DelegatingMethodAccessorImpl parent) { this.parent = parent; } private static native Object invoke0(Method m, Object obj, Object[] args);}
- 每次NativeMethodAccessorImpl.invoke() 办法被调用时,程序调用计数器都会减少1, 看看是否超过阈值
- 如果超过则调用MethodAccessorGenerator.generateMethod() 来生成Java版的MethodAccessor的实现类
- 扭转DelegatingMethodAccessorImpl所援用的MethodAccessor为Java版
经由DelegatingMethodAccessorImpl.invoke() 调用到的就是Java版的实现
JVM层invoke0办法
invoke0办法是一个native办法,在HotSpot JVM里调用JVM_InvokeMethod函数:
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args){ return JVM_InvokeMethod(env, m, obj, args);}
openjdk/hotspot/src/share/vm/prims/jvm.cpp:
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))JVMWrapper("JVM_InvokeMethod");Handle method_handle;if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver(THREAD, JNIHandles::resolve(obj)); objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0))); oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!"); if (java_lang_Class::is_primitive(ret_type)) { // Only for primitive type vm allocates memory for java object. // See box() method. JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } } return res;} else { THROW_0(vmSymbols::java_lang_StackOverflowError());}JVM_END
要害局部为Reflection::invoke_method: openjdk/hotspot/src/share/vm/runtime/reflection.cpp
oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {oop mirror = java_lang_reflect_Method::clazz(method_mirror);int slot = java_lang_reflect_Method::slot(method_mirror);bool override = java_lang_reflect_Method::override(method_mirror) != 0;objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);BasicType rtype;if (java_lang_Class::is_primitive(return_type_mirror)) { rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);} else { rtype = T_OBJECT;}instanceKlassHandle klass(THREAD, java_lang_Class::as_Klass(mirror));Method* m = klass->method_with_idnum(slot);if (m == NULL) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");}methodHandle method(THREAD, m);return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);}
Java的对象模型 :klass和oop
Java版的实现
Java版MethodAccessor的生成应用MethodAccessorGenerator实现
Generator for sun.reflect.MethodAccessor and sun.reflect.ConstructorAccessor objects using bytecodes to implement reflection. A java.lang.reflect.Method or java.lang.reflect.Constructor object can delegate its invoke or newInstance method to an accessor using native code or to one generated by this class. (Methods and Constructors were merged together in this class to ensure maximum code sharing.)
使用了asm动静生成字节码技术 - sun.reflect.ClassFileAssembler
invoke总结
- invoke办法的过程:
MagicAccessorImpl:
本来Java的平安机制使得不同类之间不是任意信息都可见,但JDK外面专门设了个MagicAccessorImpl标记类开了个后门来容许不同类之间信息能够相互拜访,由JVM治理
/** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl andothers, not because it actually implements an interface) is amarker class in the hierarchy. All subclasses of this class are"magically" granted access by the VM to otherwise inaccessiblefields and methods of other classes. It is used to hold the codefor dynamically-generated FieldAccessorImpl and MethodAccessorImplsubclasses. (Use of the word "unsafe" was avoided in this class'sname to avoid confusion with {@link sun.misc.Unsafe}.) </P><P> The bug fix for 4486457 also necessitated disablingverification for this class and all subclasses, as opposed to justSerializationConstructorAccessorImpl and subclasses, to avoidhaving to indicate to the VM which of these dynamically-generatedstub classes were known to be able to pass the verifier. </P><P> Do not change the name of this class without also changing theVM's code. </P> */class MagicAccessorImpl {}
@CallerSensitive注解
Summary: Improve the security of the JDK’s method-handle implementation by replacing the existing hand-maintained list of caller-sensitive methods with a mechanism that accurately identifiessuch methods and allows their callers to be discovered reliably.
/** * A method annotated @CallerSensitive is sensitive to its calling class, * via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass}, * or via some equivalent. * * @author John R. Rose */@Retention(RetentionPolicy.RUNTIME)@Target({METHOD})public @interface CallerSensitive {}
用 @CallerSensitive注解润饰的办法从一开始就晓得具体调用此办法的对象
- 不必再通过一系列的查看就能确定具体调用此办法的对象
- 实际上是调用sun.reflect.Reflection.getCallerClass办法
Reflection类位于调用栈中的0帧地位
- sun.reflect.Reflection.getCallerClass() 办法返回调用栈中从0帧开始的第x帧中的类实例
- 该办法提供的机制可用于确定调用者类,从而实现"感知调用者(Caller Sensitive)"的行为
即容许应用程序依据调用类或调用栈中的其它类来扭转其本身的行为
反射留神点
- 反射会额定耗费系统资源,如果不须要动静地创立一个对象,就不要应用反射
- 反射调用办法时能够疏忽权限查看.可能会毁坏封装性而导致平安问题