目錄
image.png

例子
- 定義一個(gè)User類
public class User {
private String name;
private Integer age;
// 省略get set
}
反射設(shè)置屬性
public class Test {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("User");
testReflection(clazz);
}
/**
* 如果遇到多個(gè)類都有name屬性并需要提供統(tǒng)一處理這時(shí)候就很好用
* @param clazz
*/
public static void testReflection(Class clazz) {
try {
if (clazz == null) {
System.out.println("空 clazz");
return;
}
//獲得類的實(shí)例
Object obj = clazz.newInstance();
//獲得 User 類中的指定屬性對應(yīng)的Field對象(每個(gè)屬性對應(yīng)一個(gè)Field對象)
Field field = clazz.getDeclaredField("name");
//取消屬性的訪問權(quán)限控制,即使private 屬性也可以進(jìn)行訪問
field.setAccessible(true);
//調(diào)用 getter 方法獲取屬性值
System.out.println(field.get(obj));
//調(diào)用setter 方法給屬性賦值
field.set(obj, "scott");
//調(diào)用 getter 方法獲取對應(yīng)屬性修改后的值
System.out.println(field.get(obj));
}catch (Exception e){
e.printStackTrace();
System.out.println("異常");
}
}
}
反射執(zhí)行方法
public static void main(String[] args) throws Exception {
Class clazz = User.class;
Object obj = clazz.newInstance();
//調(diào)用該對象的 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);
//調(diào)用對象的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實(shí)例對象維護(hù)了一個(gè)root引用。當(dāng)調(diào)用Method.copy()進(jìn)行方法拷貝時(shí),root指向了被拷貝的對象,通過維護(hù)root引用意圖使相同的方法始終保持只有一個(gè)methodAccessor實(shí)例。ma.invoke會(huì)先通過DelegatingMethodAccessorImpl.invoke,再到NativeMethodAccessorImpl.invoke,NativeMethodAccessorImpl最終調(diào)用native方法完成invoke()任務(wù)。
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();
// 啟動(dòng)參數(shù)-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最終調(diào)用native方法完成invoke()任務(wù)。當(dāng)numInvocations數(shù)量大于配置項(xiàng)sun.reflect.inflationThreshold(m默認(rèn)15)即類膨脹閾值時(shí), 使用MethodAccessorGenerator創(chuàng)建一個(gè)代理類對象,并且將被委托的NativeMethodAccessorImpl的parent??傮w來說,當(dāng)調(diào)用invoke()時(shí),按照默認(rèn)配置,Method首先創(chuàng)建一個(gè)DelegatingMethodAccessorImpl對象,并設(shè)置一個(gè)被委托的NativeMethodAccessorImpl對象,那么method.invoke()就被轉(zhuǎn)換成DelegatingMethodAccessorImpl.invoke(),然后又被委托給NativeMethodAccessorImp.invoke()實(shí)現(xiàn)。當(dāng)NativeMethodAccessorImp.invoke()調(diào)用次數(shù)超過一定熱度時(shí)(默認(rèn)15次),被委托方又被轉(zhuǎn)換成代理類來實(shí)現(xiàn)。圖中DelegatingMethodAccessorImpl用到代碼模式,可能代理1,也可能代理2,代理1到一定條件時(shí),代理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調(diào)用
private static native Object invoke0(Method m, Object obj, Object[] args);
- 接下來看看getDeclaredMethod, 這里特別注意privateGetDeclaredMethods,其中涉及軟引用,如果getDeclaredMethod用到軟引用緩存的,上面的MethodAccessor就是同一個(gè),即使并發(fā)導(dǎo)致生成多個(gè)GeneratedMethodAccessorXXX(GeneratedMethodAccessorXXX的類加載器其實(shí)是一個(gè)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;
}
- 總結(jié): Method.invoke的邏輯,相關(guān)的method因此被SoftReference引用,因此很容易被回收,一旦被回收,那就創(chuàng)建一個(gè)新的Method對象,再調(diào)用其invoke方法,在調(diào)用到一定次數(shù)(15次)之后,就構(gòu)建一個(gè)新的字節(jié)碼類,伴隨著GC的進(jìn)行,同一個(gè)方法的字節(jié)碼類不斷構(gòu)建,直到將Perm充滿觸發(fā)一次Full GC才得以釋放
Class.forName 和 ClassLoader區(qū)別
- 在java中Class.forName()和ClassLoader都可以對類進(jìn)行加載。ClassLoader就是遵循雙親委派模型最終調(diào)用啟動(dòng)類加載器的類加載器,實(shí)現(xiàn)的功能是“通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流”,獲取到二進(jìn)制流后放到JVM中。Class.forName()方法實(shí)際上也是調(diào)用的CLassLoader來實(shí)現(xiàn)的。
- Spring框架中的IOC的實(shí)現(xiàn)就是使用的ClassLoader,JDBC時(shí)通常是使用Class.forName()方法來加載數(shù)據(jù)庫連接驅(qū)動(dòng)。這是因?yàn)樵贘DBC規(guī)范中明確要求Driver(數(shù)據(jù)庫驅(qū)動(dòng))類必須向DriverManager注冊自己。
設(shè)計(jì)模式學(xué)習(xí)
- DelegatingMethodAccessorImpl就是用到委托模式,setDelegate, parent等關(guān)鍵字還可以改變委托的對象。委托模式是一種更高級的代理模式,委托模式可以解決一種方案的多種實(shí)現(xiàn)之間自由切換,而代理模式只能根據(jù)傳入的被代理對象來實(shí)現(xiàn)功能。
- 結(jié)構(gòu)型之代理模式
- 裝飾器模式
反射和序列化破壞單例解決方案
優(yōu)化
- 可以把查找到的緩存起來。反射調(diào)用時(shí)本身就有用弱引用進(jìn)行緩存。
