反射底层原理
反射底層原理
反射作用
首先回顧一下反射的作用,反射可以根據(jù)一個類的Class對象獲得該類的Field、Constructor、Method對象,并對這三類對象進行操作。
反射獲得Class對象的方法有三種:
- 已知類名:Clazz.class
- 已知全限定名:Class.forName(“xx.xx.Clazz”)
- 已知對象:instance.getClass()
反射底層實現(xiàn)原理
反射的緩存
利用Class對象從JVM獲得的Field、Constructor、Method對象的存儲定義在Class類中的靜態(tài)內(nèi)部類ReflectionData<T>中;Class對象持有一個ReflectData<T>的軟引用實例:
private volatile transient SoftReference<ReflectionData<T>> reflectionData;private static class ReflectionData<T> {volatile Field[] declaredFields;volatile Field[] publicFields;volatile Method[] declaredMethods;volatile Method[] publicMethods;volatile Constructor<T>[] declaredConstructors;volatile Constructor<T>[] publicConstructors;// Intermediate results for getFields and getMethodsvolatile Field[] declaredPublicFields;volatile Method[] declaredPublicMethods;volatile Class<?>[] interfaces;// Value of classRedefinedCount when we created this ReflectionData instancefinal int redefinedCount;ReflectionData(int redefinedCount) {this.redefinedCount = redefinedCount;} }由于這個屬性是軟引用的,在某些內(nèi)存比較苛刻的情況下是可能被回收的,也可以通過-XX:SoftRefLRUPolicyMSPerMB這個參數(shù)來控制回收的時機。
反射獲取的方式
以獲取類的Method為例:
- 獲取所有方法
獲取所有方法會調(diào)用getDeclaredMethods方法:
@CallerSensitive public Method[] getDeclaredMethods() throws SecurityException {checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);return copyMethods(privateGetDeclaredMethods(false)); }該方法會先檢查是否允許訪問,默認策略是允許的,然后會調(diào)用privateGetDeclaredMethods(boolean publicOnly)去進行尋找該類的方法,傳入的boolean參數(shù)決定是否只獲取定義為public的方法:
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 VMres = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));if (rd != null) {if (publicOnly) {rd.declaredPublicMethods = res;} else {rd.declaredMethods = res;}}return res; }該方法中可以看到會首先檢查reflectionData中是否已經(jīng)緩存了結(jié)果;
如果緩存中沒有,再通過getDeclaredMethods0()從虛擬機去獲取,該方法是一個native方法。
最后根據(jù)privateGetDeclaredMethods方法返回的Method數(shù)組,調(diào)用copyMethods()復制一份相同的Method數(shù)組進行返回:
private static Method[] copyMethods(Method[] arg) {Method[] out = new Method[arg.length];ReflectionFactory fact = getReflectionFactory();for (int i = 0; i < arg.length; i++) {out[i] = fact.copyMethod(arg[i]);}return out; }- 獲取某個方法
獲取某個方法會調(diào)用searchMethods方法從上面提到的privateGetDeclaredMethods返回的Method數(shù)組里找到一個同名的匹配的方法:
@CallerSensitive public Method getDeclaredMethod(String name, Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException {checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);if (method == null) {throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));}return method; }searchMethods方法并不會直接返回匹配的Method對象,而是利用反射工廠(ReflectionFactory)去復制一個新的Method對象:
private static Method searchMethods(Method[] methods, String name, Class<?>[] parameterTypes) {Method res = null;String internedName = name.intern();for (int i = 0; i < methods.length; i++) {Method m = methods[i];if (m.getName() == internedName&& arrayContentsEq(parameterTypes, m.getParameterTypes())&& (res == null|| res.getReturnType().isAssignableFrom(m.getReturnType())))res = m;}return (res == null ? res : getReflectionFactory().copyMethod(res)); }反射工廠的copyMethod()方法的具體實現(xiàn)是Method類中的copy()方法:
Method copy() {if (this.root != null)throw new IllegalArgumentException("Can not copy a non-root Method");Method res = new Method(clazz, name, parameterTypes, returnType,exceptionTypes, modifiers, slot, signature,annotations, parameterAnnotations, annotationDefault);res.root = this;// Might as well eagerly propagate this if already presentres.methodAccessor = methodAccessor;return res; }由此可見,我們每次通過調(diào)用getDeclaredMethod方法返回的Method對象其實都是一個新的對象。
利用反射進行方法的調(diào)用
獲得了Method對象之后,就可以通過Method.invoke()方法調(diào)用該方法:
@CallerSensitive 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 volatileif (ma == null) {ma = acquireMethodAccessor();}return ma.invoke(obj, args); }從上述代碼可以看到,方法的實際調(diào)用(invoke)實際是通過MethodAccessor進行的;MethodAccessor接口定義了invoke方法:
public interface MethodAccessor {Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException; }MethodAccessor有三個實現(xiàn):
-
NativeMethodAccessorImpl:使用native方法去調(diào)用方法;
-
MethodAccessorImpl:抽象類,并沒有提供invoke方法的具體實現(xiàn);
-
DelegatingMethodAccessorImpl:繼承了MethodAccessorImpl,是一個代理,可以通過構(gòu)造函數(shù)或setter方法設(shè)置具體的實現(xiàn);其invoke方法的實現(xiàn)是調(diào)用其代理實現(xiàn)的invoke:
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;} }
那么為什么要設(shè)計DelegatingMethodAccessorImpl這樣一個代理呢?
繼續(xù)往下看,會根據(jù)方法的調(diào)用次數(shù)切換MethodAccessor的具體實現(xiàn),利用DelegatingMethodAccessorImpl這樣一個代理的setter方法,就可以輕松切換。
至于為什么要切換,也留到后面再說。
那么具體是使用哪個MethodAccessor來實現(xiàn)方法的調(diào)用的呢?
回到上面的Method.invoke()方法中,會調(diào)用acquireMethodAccessor方法去獲取MethodAccessor,我們來詳細分析獲取的過程:
private MethodAccessor acquireMethodAccessor() {// First check to see if one has been created yet, and take it// if soMethodAccessor tmp = null;if (root != null) tmp = root.getMethodAccessor();if (tmp != null) {methodAccessor = tmp;} else {// Otherwise fabricate one and propagate it up to the roottmp = reflectionFactory.newMethodAccessor(this);setMethodAccessor(tmp);}return tmp; }這個方法很簡單,首先查詢緩存(Method類的一個字段);如果沒有獲取過,就調(diào)用反射工廠的newMethodAccessor方法去創(chuàng)建一個,并存到對象對應(yīng)的字段中去;
我們來看newMethodAccessor方法:
public MethodAccessor newMethodAccessor(Method var1) {checkInitted();if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {// 利用字節(jié)碼生成一個MethodAccessorreturn (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());} else {// 使用NativeMethodAccessorNativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);var2.setParent(var3);return var3;} }checkInitted方法我們留到后面再講,首先看生成的MethodAccessor,會有兩種可能:
- 如果noInflation參數(shù)為true(默認為false,后面再介紹具體含義),另一個參數(shù)不去管它:會由MethodAccessorGenerator根據(jù)字節(jié)碼相當于生成一個虛擬的Class對象來調(diào)用其相應(yīng)的方法;(這種方法性能會好很多,相當于直接調(diào)用一個類的某個方法)
- 否則,MethodAccessor實際是一個代理NativeMethodAccessor的DelegatingMethodAccessor(實際調(diào)用native方法的invoke)。
根據(jù)上面的分析,可以得知實際的MethodAccessor實現(xiàn)有兩個版本,一個是Java實現(xiàn)的,另一個是native實現(xiàn)的。它們兩個的性能各有優(yōu)劣:Java實現(xiàn)的版本會在初始化時需要較多時間,但長久來說性能較好;native版本正好相反,啟動時相對較快,但運行時間長了之后速度就比不過Java版了。
那么如何權(quán)衡兩種實現(xiàn)呢?根據(jù)邏輯我們也可以輕松想到,就是通過一個參數(shù)來統(tǒng)計使用次數(shù):如果偶爾使用,我們就使用native版本;如果經(jīng)常使用,就用jdk版本。
jdk中關(guān)于這個參數(shù)有兩個設(shè)置,上面newMethodAccessor方法中我們還沒具體介紹的checkInitted方法就是去檢查這個參數(shù)的,該參數(shù)有兩種設(shè)置:
- -Dsun.reflect.inflationThreshold=xxx:默認15,表示超過15次使用JDK版本的MethodAccessor。
- -Dsun.reflect.noInflation=true:直接不使用native版本的;我們從newMethodAccessor方法中也可以看到如果noInflation為true就直接創(chuàng)建JDK版本的。
根據(jù)默認的noInflation為false,會在15次調(diào)用之前使用NatvieMethodAccessor,在該類中會對調(diào)用次數(shù)進行統(tǒng)計,當超過15次時,就會調(diào)用代理(DelegatingMethodAccerssorImpl)的setter方法切換成JDK版本的,這也就是之前提到的這個代理的作用,具體請看下面代碼:
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 {// 每次調(diào)用invoke之前先計數(shù)if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {// 如果超過了inflationThreshold設(shè)置的值,就會切換成JDK版本的MethodAccessor實現(xiàn)MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());// 利用代理的setter方法實現(xiàn)切換this.parent.setDelegate(var3);}// 使用native版本的invokereturn invoke0(this.method, var1, var2);}void setParent(DelegatingMethodAccessorImpl var1) {this.parent = var1;}private static native Object invoke0(Method var0, Object var1, Object[] var2); }總結(jié)
- 上一篇: 自考计算机原理知识点,(完整版)18版自
- 下一篇: 智慧船舶,在青春的赛道上奋力奔跑