目錄:
一.反射基礎(chǔ)
二.反射的作用
三.反射機制執(zhí)行的流程
一.反射基礎(chǔ)
什么是反射剩彬?
反射使 Java 代碼可以發(fā)現(xiàn)有關(guān)已加載類的字段蛤吓,方法和構(gòu)造函數(shù)的信息往枷,并在安全性限制內(nèi)使用反射對這些字段蝌矛,方法和構(gòu)造函數(shù)進行操作菲驴。
反射的概念是由Smith在1982年首次提出的荐吵,主要是指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力赊瞬。這一概念的提出很快引發(fā)了計算機科學(xué)領(lǐng)域關(guān)于應(yīng)用反射性的研究先煎。它首先被程序語言的設(shè)計領(lǐng)域所采用,并在Lisp和面向?qū)ο蠓矫嫒〉昧顺煽儭F渲蠰EAD/LEAD++ 巧涧、OpenC++ 薯蝎、MetaXa和OpenJava等就是基于反射機制的語言。最近谤绳,反射機制也被應(yīng)用到了視窗系統(tǒng)良风、操作系統(tǒng)和文件系統(tǒng)中。
Java中闷供,反射是一種強大的工具烟央。它使您能夠創(chuàng)建靈活的代碼,這些代碼可以在運行時裝配歪脏,無需在組件之間進行源代表鏈接疑俭。反射允許我們在編寫與執(zhí)行時,使我們的程序代碼能夠接入裝載到JVM中的類的內(nèi)部信息婿失,而不是源代碼中選定的類協(xié)作的代碼钞艇。這使反射成為構(gòu)建靈活的應(yīng)用的主要工具。但需注意的是:如果使用不當豪硅,反射的成本很高哩照。
簡而言之,指在 Java 程序運行時
- 給定的一個類(Class)對象懒浮,通過反射獲取這個類(Class)對象的所有成員結(jié)構(gòu)飘弧。
- 給定的一個具體的對象识藤,能夠動態(tài)地調(diào)用它的方法及對任意屬性值進行獲取和賦值。
這種動態(tài)獲取類的內(nèi)容次伶,創(chuàng)建對象痴昧、以及動態(tài)調(diào)用對象的方法及操作屬性的機制為反射。即使該對象的類型在編譯期間是未知冠王,該類的 .class 文件不存在赶撰,也可以通過反射直接創(chuàng)建對象。
優(yōu)勢
- 增加程序的靈活性柱彻,避免將固有的邏輯程序?qū)懰赖酱a里
- 代碼簡潔豪娜,可讀性強,可提高代碼的復(fù)用率
劣勢
- 相較直接調(diào)用哟楷,在量大的情景下反射性能下降
- 存在一些內(nèi)部暴露和安全隱患
為什么要有反射
有了反射瘤载,我們可以做以下事情:
- 在運行時檢查一個對象
- 在運行時,根據(jù)一個class構(gòu)造一個對象
- 在運行時吓蘑,檢查一個對象的屬性和方法
- 在運行時惕虑,調(diào)用一個對象的任意一個方法
- 在運行時,改變對象的構(gòu)造函數(shù)磨镶,屬性溃蔫,方法的可見性等等
反射是很多框架的共有的方法:
- 例如JUnit,就是使用反射去找出那些帶有@Test注解的方法琳猫,然后就利用反射在單元測試中調(diào)用這些方法
- 在web框架中伟叛,開發(fā)人員將他們定義實現(xiàn)的接口和類放到配置文件中,使用反射脐嫂,他可以動態(tài)地在運行時自動初始化這些類和接口 例如统刮,Spring中一般這樣使用配置文件:
<bean id="someID" class="com.programcreek.Foo">
<property name="someField" value="someValue" /></bean>
當Spring讀取到bean文件的時候,會調(diào)用Class.forName(String)方法"com.programcreek.Foo"來初始化這個類账千,然后在使用反射正確的get到所配置的屬性的set方法侥蒙,并把相應(yīng)的值set進去。
Servlet web 也是使用這種反射技術(shù):
<servlet>
<servlet-name>someServlet</servlet-name>
<servlet-class>com.programcreek.WhyReflectionServlet</servlet-class><servlet>
反射的原理(類加載)
關(guān)于類加載機制匀奏,大家可以參考我的這篇文章:
深入理解JVM虛擬機——類的加載機制
深入理解JVM虛擬機——JVM是如何實現(xiàn)反射的鞭衩?
類加載機制流程
類的加載
反射的原理圖解
二. 反射的作用
一個類的成員包括以下三種:域信息、構(gòu)造器信息娃善、方法信息论衍。而反射則可以在運行時動態(tài)獲取到這些信息,在使用反射時聚磺,我們常用的類有以下五種坯台。
Class類對象的獲取
1、獲得Class:主要有三種方法:
(1)Object–>getClass
(2)任何數(shù)據(jù)類型(包括基本的數(shù)據(jù)類型)都有一個“靜態(tài)”的class屬性
(3)通過class類的靜態(tài)方法:forName(String className)(最常用)
package fanshe;
public class Fanshe {
public static void main(String[] args) {
//第一種方式獲取Class對象
Student stu1 = new Student();//這一new 產(chǎn)生一個Student對象瘫寝,一個Class對象蜒蕾。
Class stuClass = stu1.getClass();//獲取Class對象
System.out.println(stuClass.getName());
//第二種方式獲取Class對象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判斷第一種方式獲取的Class對象和第二種方式獲取的是否是同一個
//第三種方式獲取Class對象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必須是真實路徑稠炬,就是帶包名的類路徑,包名.類名
System.out.println(stuClass3 == stuClass2);//判斷三種方式是否獲取的是同一個Class對象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
注意滥搭,在運行期間酸纲,一個類捣鲸,只有一個Class對象產(chǎn)生瑟匆,所以打印結(jié)果都是true;
三種方式中栽惶,常用第三種愁溜,第一種對象都有了還要反射干什么,第二種需要導(dǎo)入類包外厂,依賴太強冕象,不導(dǎo)包就拋編譯錯誤。一般都使用第三種汁蝶,一個字符串可以傳入也可以寫在配置文件中等多種方法渐扮。
Class類的方法
getName、getCanonicalName與getSimpleName的區(qū)別:
- getSimpleName:只獲取類名
- getName:類的全限定名掖棉,jvm中Class的表示墓律,可以用于動態(tài)加載Class對象,例如Class.forName幔亥。
- getCanonicalName:返回更容易理解的表示耻讽,主要用于輸出(toString)或log打印,大多數(shù)情況下和getName一樣帕棉,但是在內(nèi)部類针肥、數(shù)組等類型的表示形式就不同了。
Constructor類及其獲取對象方法
- Constructor提供了一個類的單個構(gòu)造函數(shù)的信息和訪問香伴。
- Constructor允許在將實際參數(shù)與newInstance()與底層構(gòu)造函數(shù)的形式參數(shù)進行匹配時進行擴展轉(zhuǎn)換慰枕,但如果發(fā)生縮小轉(zhuǎn)換,則拋出IllegalArgumentException 即纲。
Constructor類的方法
獲取Constructor對象是通過Class類中的方法獲取的具帮,Class類與Constructor相關(guān)的主要方法如下:
使用反射技術(shù)獲取構(gòu)造器對象并使用
@Test
public void test2() throws NoSuchMethodException {
Class<Student> sc = Student.class;
// 1. 拿到所有的構(gòu)造器
Constructor<?>[] constructors = sc.getDeclaredConstructors();
// 輸出構(gòu)造器的名稱+參數(shù)個數(shù)
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName() + " 參數(shù)個數(shù):" + constructor.getParameterCount() + "個");
}
// 2. 拿到單個構(gòu)造器
Constructor<Student> constructor = sc.getDeclaredConstructor(String.class, String.class);
System.out.println(constructor.getName() + "參數(shù)個數(shù):" + constructor.getParameterCount());
}
使用反射技術(shù)獲取構(gòu)造器對象并使用獲取到的內(nèi)容創(chuàng)建出一個對象
反射得到構(gòu)造器之后的作用仍是創(chuàng)建一個對象,如果說構(gòu)造器是public崇裁,就可以直接new對象匕坯,如果說是構(gòu)造器是私有的private,需要提前將構(gòu)造器進行暴力反射拔稳,再進行構(gòu)造對象葛峻。
反射是可以直接破換掉封裝性的,私有的也是可以執(zhí)行的巴比。
Field類及其用法
Field 提供有關(guān)類或接口的單個字段的信息术奖,以及對它的動態(tài)訪問權(quán)限礁遵。反射的字段可能是一個類(靜態(tài))字段或?qū)嵗侄巍?/span>
Field類涉及的get方法
同樣的道理,我們可以通過Class類的提供的方法來獲取代表字段信息的Field對象采记,Class類與Field對象相關(guān)方法如下:
下面的代碼演示了上述方法的使用過程
public class ReflectField {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> clazz = Class.forName("reflect.Student");
//獲取指定字段名稱的Field類,注意字段修飾符必須為public而且存在該字段,
// 否則拋NoSuchFieldException
Field field = clazz.getField("age");
System.out.println("field:" + field);
//獲取所有修飾符為public的字段,包含父類字段,注意修飾符為public才會獲取
Field fields[] = clazz.getFields();
for (Field f : fields) {
System.out.println("f:" + f.getDeclaringClass());
}
System.out.println("================getDeclaredFields====================");
//獲取當前類所字段(包含private字段),注意不包含父類的字段
Field fields2[] = clazz.getDeclaredFields();
for (Field f : fields2) {
System.out.println("f2:" + f.getDeclaringClass());
}
//獲取指定字段名稱的Field類,可以是任意修飾符的自動,注意不包含父類的字段
Field field2 = clazz.getDeclaredField("desc");
System.out.println("field2:" + field2);
}
/**
輸出結(jié)果:
field:public int reflect.Person.age
f:public java.lang.String reflect.Student.desc
f:public int reflect.Person.age
f:public java.lang.String reflect.Person.name
================getDeclaredFields====================
f2:public java.lang.String reflect.Student.desc
f2:private int reflect.Student.score
field2:public java.lang.String reflect.Student.desc
*/
}
class Person {
public int age;
public String name;
//省略set和get方法}
class Student extends Person {
public String desc;
private int score;
//省略set和get方法
}
上述方法需要注意的是佣耐,如果我們不期望獲取其父類的字段,則需使用Class類的getDeclaredField/getDeclaredFields方法來獲取字段即可唧龄,倘若需要連帶獲取到父類的字段兼砖,那么請使用Class類的getField/getFields,但是也只能獲取到public修飾的的字段既棺,無法獲取父類的私有字段讽挟。下面將通過Field類本身的方法對指定類屬性賦值,代碼演示如下:
//獲取Class對象引用
Class<?> clazz = Class.forName("reflect.Student");
Student st= (Student) clazz.newInstance();
//獲取父類public字段并賦值
Field ageField = clazz.getField("age");
ageField.set(st,18);
Field nameField = clazz.getField("name");
nameField.set(st,"Lily");
//只獲取當前類的字段,不獲取父類的字段
Field descField = clazz.getDeclaredField("desc");
descField.set(st,"I am student");Field scoreField = clazz.getDeclaredField("score");
//設(shè)置可訪問丸冕,score是private的
scoreField.setAccessible(true);
scoreField.set(st,88);System.out.println(st.toString());
//輸出結(jié)果:Student{age=18, name='Lily ,desc='I am student', score=88}
//獲取字段值System.out.println(scoreField.get(st));// 88
其中的set(Object obj, Object value)方法是Field類本身的方法耽梅,用于設(shè)置字段的值,而get(Object obj)則是獲取字段的值胖烛,當然關(guān)于Field類還有其他常用的方法如下:
上述方法可能是較為常用的眼姐,事實上在設(shè)置值的方法上,F(xiàn)ield類還提供了專門針對基本數(shù)據(jù)類型的方法佩番,如setInt()/getInt()众旗、setBoolean()/getBoolean、setChar()/getChar()等等方法答捕,這里就不全部列出了逝钥,需要時查API文檔即可。需要特別注意的是被final關(guān)鍵字修飾的Field字段是安全的拱镐,在運行時可以接收任何修改艘款,但最終其實際值是不會發(fā)生改變的。
Method類及其用法
Method 提供關(guān)于類或接口上單獨某個方法(以及如何訪問該方法)的信息沃琅,所反映的方法可能是類方法或?qū)嵗椒ǎòǔ橄蠓椒ǎ?/span>
Method類的主要方法
下面是Class類獲取Method對象相關(guān)的方法:
同樣通過案例演示上述方法:
import java.lang.reflect.Method;
public class ReflectMethod {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("reflect.Circle");
//根據(jù)參數(shù)獲取public的Method,包含繼承自父類的方法
Method method = clazz.getMethod("draw", int.class, String.class);
System.out.println("method:" + method);
//獲取所有public的方法:
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println("m::" + m);
}
System.out.println("=========================================");
//獲取當前類的方法包含private,該方法無法獲取繼承自父類的method
Method method1 = clazz.getDeclaredMethod("drawCircle");
System.out.println("method1::" + method1);
//獲取當前類的所有方法包含private,該方法無法獲取繼承自父類的method
Method[] methods1 = clazz.getDeclaredMethods();
for (Method m : methods1) {
System.out.println("m1::" + m);
}
}
}
class Shape {
public void draw() {
System.out.println("draw");
}
public void draw(int count, String name) {
System.out.println("draw " + name + ",count=" + count);
}
}
class Circle extends Shape {
private void drawCircle() {
System.out.println("drawCircle");
}
public int getAllCount() {
return 100;
}
}
輸出結(jié)果:
method:public void reflect.Shape.draw(int,java.lang.String)
m::public int reflect.Circle.getAllCount()
m::public void reflect.Shape.draw()
m::public void reflect.Shape.draw(int,java.lang.String)
m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
m::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
m::public final void java.lang.Object.wait() throws java.lang.InterruptedException
m::public boolean java.lang.Object.equals(java.lang.Object)
m::public java.lang.String java.lang.Object.toString()
m::public native int java.lang.Object.hashCode()
m::public final native java.lang.Class java.lang.Object.getClass()
m::public final native void java.lang.Object.notify()
m::public final native void java.lang.Object.notifyAll()
=========================================
method1::private void reflect.Circle.drawCircle()
m1::public int reflect.Circle.getAllCount()
m1::private void reflect.Circle.drawCircle()
在通過getMethods方法獲取Method對象時哗咆,會把父類的方法也獲取到,如上的輸出結(jié)果益眉,把Object類的方法都打印出來了晌柬。而getDeclaredMethod/getDeclaredMethods方法都只能獲取當前類的方法。我們在使用時根據(jù)情況選擇即可郭脂。下面將演示通過Method對象調(diào)用指定類的方法:
Class clazz = Class.forName("reflect.Circle");
//創(chuàng)建對象
Circle circle = (Circle) clazz.newInstance();
//獲取指定參數(shù)的方法對象
MethodMethod method = clazz.getMethod("draw",int.class,String.class);
//通過Method對象的invoke(Object obj,Object... args)方法調(diào)用
method.invoke(circle,15,"圈圈");
//對私有無參方法的操作
Method method1 = clazz.getDeclaredMethod("drawCircle");
//修改私有方法的訪問標識
method1.setAccessible(true);
method1.invoke(circle);
//對有返回值得方法操作
Method method2 =clazz.getDeclaredMethod("getAllCount");
Integer count = (Integer) method2.invoke(circle);
System.out.println("count:"+count);
輸出結(jié)果
draw 圈圈,count=15
drawCircle
count:100
在上述代碼中調(diào)用方法年碘,使用了Method類的invoke(Object obj,Object... args)第一個參數(shù)代表調(diào)用的對象,第二個參數(shù)傳遞的調(diào)用方法的參數(shù)展鸡。這樣就完成了類方法的動態(tài)調(diào)用屿衅。
三. 反射機制執(zhí)行的流程
-- 測試代碼
-- 執(zhí)行流程圖
反射獲取類實例
首先調(diào)用了 java.lang.Class 的靜態(tài)方法,獲取類信息莹弊。
@CallerSensitive
public static Class<?> forName(String className) throws ClassNotFoundException {
// 先通過反射涤久,獲取調(diào)用進來的類信息涡尘,從而獲取當前的 classLoader
Class<?> caller = Reflection.getCallerClass();
// 調(diào)用native方法進行獲取class信息
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
forName()反射獲取類信息,并沒有將實現(xiàn)留給了java,而是交給了jvm去加載响迂。
主要是先獲取 ClassLoader, 然后調(diào)用 native 方法考抄,獲取信息,加載類則是回調(diào) java.lang.ClassLoader.
最后蔗彤,jvm又會回調(diào) ClassLoader 進類加載川梅。
//
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
// sun.misc.Launcher
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if(var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if(var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
if(this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if(var5 != null) {
if(var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
// java.lang.ClassLoader
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 先獲取鎖
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 如果已經(jīng)加載了的話,就不用再加載了
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 雙親委托加載
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 父類沒有加載到時幕与,再自己加載
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
// 使用 ConcurrentHashMap來保存鎖
Object newLock = new Object();
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
下面來看一下 newInstance() 的實現(xiàn)方式挑势。
// 首先肯定是 Class.newInstance
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException {
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
// newInstance() 其實相當于調(diào)用類的無參構(gòu)造函數(shù)镇防,所以啦鸣,首先要找到其無參構(gòu)造器
if (cachedConstructor == null) {
if (this == Class.class) {
// 不允許調(diào)用 Class 的 newInstance() 方法
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
// 獲取無參構(gòu)造器
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
// 調(diào)用無參構(gòu)造器
return tmpConstructor.newInstance((Object[]) null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
newInstance() 主要做了三件事:
- 權(quán)限檢測,如果不通過直接拋出異常来氧;查找無參構(gòu)造器诫给,并將其緩存起來;調(diào)用具體方法的無參構(gòu)造方法啦扬,生成實例并返回中狂;
下面是獲取構(gòu)造器的過程:
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
// 獲取所有構(gòu)造器
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
getConstructor0() 為獲取匹配的構(gòu)造方器;分三步:
- 先獲取所有的constructors, 然后通過進行參數(shù)類型比較扑毡;找到匹配后胃榕,通過 ReflectionFactory copy一份constructor返回;否則拋出 NoSuchMethodException;
// 獲取當前類所有的構(gòu)造方法瞄摊,通過jvm或者緩存
// Returns an array of "root" constructors. These Constructor
// objects must NOT be propagated to the outside world, but must
// instead be copied via ReflectionFactory.copyConstructor.
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
checkInitted();
Constructor<T>[] res;
// 調(diào)用 reflectionData(), 獲取保存的信息勋又,使用軟引用保存,從而使內(nèi)存不夠可以回收
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
// 存在緩存换帜,則直接返回
if (res != null) return res;
}
// No cached value available; request value from VM
if (isInterface()) {
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
// 使用native方法從jvm獲取構(gòu)造器
res = getDeclaredConstructors0(publicOnly);
}
if (rd != null) {
// 最后楔壤,將從jvm中讀取的內(nèi)容,存入緩存
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (useCaches &&
reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, classRedefinedCount);
}
// 新創(chuàng)建緩存惯驼,保存反射信息
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
int classRedefinedCount) {
if (!useCaches) return null;
// 使用cas保證更新的線程安全性蹲嚣,所以反射是保證線程安全的
while (true) {
ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
// try to CAS it...
if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
return rd;
}
// 先使用CAS更新,如果更新成功祟牲,則立即返回隙畜,否則測查當前已被其他線程更新的情況,如果和自己想要更新的狀態(tài)一致说贝,則也算是成功了
oldReflectionData = this.reflectionData;
classRedefinedCount = this.classRedefinedCount;
if (oldReflectionData != null &&
(rd = oldReflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
}
}
如上议惰,privateGetDeclaredConstructors(), 獲取所有的構(gòu)造器主要步驟;
- 先嘗試從緩存中獲瓤袼俊换淆;如果緩存沒有哗总,則從jvm中重新獲取,并存入緩存倍试,緩存使用軟引用進行保存讯屈,保證內(nèi)存可用;
另外县习,使用 relactionData() 進行緩存保存涮母;ReflectionData 的數(shù)據(jù)結(jié)構(gòu)如下。
// reflection data that might get invalidated when JVM TI RedefineClasses() is called
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 getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
其中躁愿,還有一個點叛本,就是如何比較構(gòu)造是否是要查找構(gòu)造器,其實就是比較類型完成相等就完了彤钟,有一個不相等則返回false来候。
private static boolean arrayContentsEq(Object[] a1, Object[] a2) {
if (a1 == null) {
return a2 == null || a2.length == 0;
}
if (a2 == null) {
return a1.length == 0;
}
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) {
return false;
}
}
return true;
}
// sun.reflect.ReflectionFactory
/**
* Makes a copy of the passed constructor. The returned
* <p>
* constructor is a "child" of the passed one; see the comments
* <p>
* in Constructor.java for details.
*/
public <T> Constructor<T> copyConstructor(Constructor<T> arg) {
return langReflectAccess().copyConstructor(arg);
}
// java.lang.reflect.Constructor, copy 其實就是新new一個 Constructor 出來
Constructor<T> copy() {
// This routine enables sharing of ConstructorAccessor objects
// among Constructor objects which refer to the same underlying
// method in the VM. (All of this contortion is only necessary
// because of the "accessibility" bit in AccessibleObject,
// which implicitly requires that new java.lang.reflect
// objects be fabricated for each reflective call on Class
// objects.)
if (this.root != null)
throw new IllegalArgumentException("Can not copy a non-root Constructor");
Constructor<T> res = new Constructor<>(clazz,
parameterTypes,
exceptionTypes, modifiers, slot,
signature,
annotations,
parameterAnnotations);
// root 指向當前 constructor
res.root = this;
// Might as well eagerly propagate this if already present
res.constructorAccessor = constructorAccessor;
return res;
}
通過上面,獲取到 Constructor 了逸雹。
接下來就只需調(diào)用其相應(yīng)構(gòu)造器的 newInstance()营搅,即返回實例了。
// return tmpConstructor.newInstance((Object[])null);
// java.lang.reflect.Constructor
@CallerSensitive
public T newInstance(Object... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
// sun.reflect.DelegatingConstructorAccessorImpl
public Object newInstance(Object[] args)
throws InstantiationException,
IllegalArgumentException,
InvocationTargetException {
return delegate.newInstance(args);
}
// sun.reflect.NativeConstructorAccessorImpl
public Object newInstance(Object[] args)
throws InstantiationException,
IllegalArgumentException,
InvocationTargetException {
// We can't inflate a constructor belonging to a vm-anonymous class
// 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(c.getDeclaringClass())) {
ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
new MethodAccessorGenerator().
generateConstructor(c.getDeclaringClass(),
c.getParameterTypes(),
c.getExceptionTypes(),
c.getModifiers());
parent.setDelegate(acc);
}
// 調(diào)用native方法梆砸,進行調(diào)用 constructor
return newInstance0(c, args);
}
返回構(gòu)造器的實例后转质,可以根據(jù)外部進行進行類型轉(zhuǎn)換,從而使用接口或方法進行調(diào)用實例功能了帖世。
反射獲取方法
- 第一步休蟹,先獲取 Method;
// java.lang.Class
@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;
}
忽略第一個檢查權(quán)限,剩下就只有兩個動作了日矫。
- 獲取所有方法列表赂弓;根據(jù)方法名稱和方法列表,選出符合要求的方法搬男;如果沒有找到相應(yīng)方法拣展,拋出異常,否則返回對應(yīng)方法缔逛;
所以备埃,先看一下怎樣獲取類聲明的所有方法?
// Returns an array of "root" methods. These Method objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
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;
}
很相似褐奴,和獲取所有構(gòu)造器的方法很相似按脚,都是先從緩存中獲取方法,如果沒有敦冬,則從jvm中獲取辅搬。
不同的是,方法列表需要進行過濾 Reflection.filterMethods;當然后面看來,這個方法我們一般不會派上用場堪遂。
// sun.misc.Reflection
public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
if (methodFilterMap == null) {
// Bootstrapping
return methods;
}
return (Method[]) filter(methods, methodFilterMap.get(containingClass));
}
// 可以過濾指定的方法介蛉,一般為空,如果要指定過濾溶褪,可以調(diào)用 registerMethodsToFilter(), 或者...
private static Member[] filter(Member[] members, String[] filteredNames) {
if ((filteredNames == null) || (members.length == 0)) {
return members;
}
int numNewMembers = 0;
for (Member member : members) {
boolean shouldSkip = false;
for (String filteredName : filteredNames) {
if (member.getName() == filteredName) {
shouldSkip = true;
break;
}
}
if (!shouldSkip) {
++numNewMembers;
}
}
Member[] newMembers =
(Member[]) Array.newInstance(members[0].getClass(), numNewMembers);
int destIdx = 0;
for (Member member : members) {
boolean shouldSkip = false;
for (String filteredName : filteredNames) {
if (member.getName() == filteredName) {
shouldSkip = true;
break;
}
}
if (!shouldSkip) {
newMembers[destIdx++] = member;
}
}
return newMembers;
}
- 第二步币旧,根據(jù)方法名和參數(shù)類型過濾指定方法返回:
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes) {
Method res = null;
// 使用常量池,避免重復(fù)創(chuàng)建String
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));
}
大概意思看得明白猿妈,就是匹配到方法名吹菱,然后參數(shù)類型匹配,才可以。
- 但是可以看到,匹配到一個方法喇伯,并沒有退出for循環(huán),而是繼續(xù)進行匹配输瓜。
- 這里是匹配最精確的子類進行返回(最優(yōu)匹配)
- 最后,還是通過 ReflectionFactory, copy 方法后返回蚌成。
調(diào)用 method.invoke() 方法
@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 volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
invoke時前痘,是通過 MethodAccessor 進行調(diào)用的,而 MethodAccessor 是個接口担忧,在第一次時調(diào)用 acquireMethodAccessor() 進行新創(chuàng)建。
// 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瓶盛,否則調(diào)用 ReflectionFactory 創(chuàng)建新的 MethodAccessor
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
return tmp;
}
// sun.reflect.ReflectionFactory
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;
}
}
兩個Accessor詳情:
// NativeMethodAccessorImpl / DelegatingMethodAccessorImplclass NativeMethodAccessorImpl extends MethodAccessorImpl {
private final 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);
}
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;
}
進行 ma.invoke(obj, args); 調(diào)用時,調(diào)用 DelegatingMethodAccessorImpl.invoke();
最后被委托到 NativeMethodAccessorImpl.invoke(), 即:
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);
}
// invoke0 是個 native 方法示罗,由jvm進行調(diào)用業(yè)務(wù)方法惩猫。從而完成反射調(diào)用功能。
return invoke0(method, obj, args);
}
其中蚜点, generateMethod() 是生成具體類的方法:
/**
* This routine is not thread-safe
*/
public MethodAccessor generateMethod(Class<?> declaringClass,
String name,
Class<?>[] parameterTypes,
Class<?> returnType,
Class<?>[] checkedExceptions,
int modifiers) {
return (MethodAccessor) generate(declaringClass,
name,
parameterTypes,
returnType,
checkedExceptions,
modifiers,
false,
false,
null);
}
generate() 戳詳情轧房。
/**
* This routine is not thread-safe
*/
private MagicAccessorImpl generate(final Class<?> declaringClass,
String name,
Class<?>[]parameterTypes,
Class<?> returnType,
Class<?>[]checkedExceptions,
int modifiers,
boolean isConstructor,
boolean forSerialization,
Class<?> serializationTargetClass)
{
ByteVector vec=ByteVectorFactory.create();
asm=new ClassFileAssembler(vec);
this.declaringClass=declaringClass;
this.parameterTypes=parameterTypes;
this.returnType=returnType;
this.modifiers=modifiers;
this.isConstructor=isConstructor;
this.forSerialization=forSerialization;
asm.emitMagicAndVersion();
// Constant pool entries:
// ( * = Boxing information: optional)
// (+ = Shared entries provided by AccessorGenerator)
// (^ = Only present if generating SerializationConstructorAccessor)
// [UTF-8] [This class's name]
// [CONSTANT_Class_info] for above
// [UTF-8] "sun/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}"
// [CONSTANT_Class_info] for above
// [UTF-8] [Target class's name]
// [CONSTANT_Class_info] for above
// ^ [UTF-8] [Serialization: Class's name in which to invoke constructor]
// ^ [CONSTANT_Class_info] for above
// [UTF-8] target method or constructor name
// [UTF-8] target method or constructor signature
// [CONSTANT_NameAndType_info] for above
// [CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info] for target method
// [UTF-8] "invoke" or "newInstance"
// [UTF-8] invoke or newInstance descriptor
// [UTF-8] descriptor for type of non-primitive parameter 1
// [CONSTANT_Class_info] for type of non-primitive parameter 1
// ...
// [UTF-8] descriptor for type of non-primitive parameter n
// [CONSTANT_Class_info] for type of non-primitive parameter n
// + [UTF-8] "java/lang/Exception"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/ClassCastException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/NullPointerException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/IllegalArgumentException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/InvocationTargetException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "<init>"
// + [UTF-8] "()V"
// + [CONSTANT_NameAndType_info] for above
// + [CONSTANT_Methodref_info] for NullPointerException's constructor
// + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor
// + [UTF-8] "(Ljava/lang/String;)V"
// + [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/String;)V"
// + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String
// + [UTF-8] "(Ljava/lang/Throwable;)V"
// + [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/Throwable;)V"
// + [CONSTANT_Methodref_info] for InvocationTargetException's constructor
// + [CONSTANT_Methodref_info] for "super()"
// + [UTF-8] "java/lang/Object"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "toString"
// + [UTF-8] "()Ljava/lang/String;"
// + [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;"
// + [CONSTANT_Methodref_info] for Object's toString method
// + [UTF-8] "Code"
// + [UTF-8] "Exceptions"
// * [UTF-8] "java/lang/Boolean"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(Z)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "booleanValue"
// * [UTF-8] "()Z"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Byte"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(B)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "byteValue"
// * [UTF-8] "()B"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Character"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(C)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "charValue"
// * [UTF-8] "()C"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Double"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(D)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "doubleValue"
// * [UTF-8] "()D"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Float"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(F)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "floatValue"
// * [UTF-8] "()F"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Integer"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(I)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "intValue"
// * [UTF-8] "()I"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Long"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(J)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "longValue"
// * [UTF-8] "()J"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Short"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(S)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "shortValue"
// * [UTF-8] "()S"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
short numCPEntries=NUM_BASE_CPOOL_ENTRIES+NUM_COMMON_CPOOL_ENTRIES;
boolean usesPrimitives=usesPrimitiveTypes();
if(usesPrimitives){
numCPEntries+=NUM_BOXING_CPOOL_ENTRIES;
}
if(forSerialization){
numCPEntries+=NUM_SERIALIZATION_CPOOL_ENTRIES;
}
// Add in variable-length number of entries to be able to describe
// non-primitive parameter types and checked exceptions.
numCPEntries+=(short)(2*numNonPrimitiveParameterTypes());
asm.emitShort(add(numCPEntries,S1));
final String generatedName=generateName(isConstructor,forSerialization);
asm.emitConstantPoolUTF8(generatedName);
asm.emitConstantPoolClass(asm.cpi());
thisClass=asm.cpi();
if(isConstructor){
if(forSerialization){
asm.emitConstantPoolUTF8
("sun/reflect/SerializationConstructorAccessorImpl");
}else{
asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
}
}else{
asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
}
asm.emitConstantPoolClass(asm.cpi());
superClass=asm.cpi();
asm.emitConstantPoolUTF8(getClassName(declaringClass,false));
asm.emitConstantPoolClass(asm.cpi());
targetClass=asm.cpi();
short serializationTargetClassIdx=(short)0;
if(forSerialization){
asm.emitConstantPoolUTF8(getClassName(serializationTargetClass,false));
asm.emitConstantPoolClass(asm.cpi());
serializationTargetClassIdx=asm.cpi();
}
asm.emitConstantPoolUTF8(name);
asm.emitConstantPoolUTF8(buildInternalSignature());
asm.emitConstantPoolNameAndType(sub(asm.cpi(),S1),asm.cpi());
if(isInterface()){
asm.emitConstantPoolInterfaceMethodref(targetClass,asm.cpi());
}else{
if(forSerialization){
asm.emitConstantPoolMethodref(serializationTargetClassIdx,asm.cpi());
}else{
asm.emitConstantPoolMethodref(targetClass,asm.cpi());
}
}
targetMethodRef=asm.cpi();
if(isConstructor){
asm.emitConstantPoolUTF8("newInstance");
}else{
asm.emitConstantPoolUTF8("invoke");
}
invokeIdx=asm.cpi();
if(isConstructor){
asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
}else{
asm.emitConstantPoolUTF8
("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
}
invokeDescriptorIdx=asm.cpi();
// Output class information for non-primitive parameter types
nonPrimitiveParametersBaseIdx=add(asm.cpi(),S2);
for(int i=0;i<parameterTypes.length;i++){
Class<?> c=parameterTypes[i];
if(!isPrimitive(c)){
asm.emitConstantPoolUTF8(getClassName(c,false));
asm.emitConstantPoolClass(asm.cpi());
}
}
// Entries common to FieldAccessor, MethodAccessor and ConstructorAccessor
emitCommonConstantPoolEntries();
// Boxing entries
if(usesPrimitives){
emitBoxingContantPoolEntries();
}
if(asm.cpi()!=numCPEntries){
throw new InternalError("Adjust this code (cpi = "+asm.cpi()+
", numCPEntries = "+numCPEntries+")");
}
// Access flags
asm.emitShort(ACC_PUBLIC);
// This class
asm.emitShort(thisClass);
// Superclass
asm.emitShort(superClass);
// Interfaces count and interfaces
asm.emitShort(S0);
// Fields count and fields
asm.emitShort(S0);
// Methods count and methods
asm.emitShort(NUM_METHODS);
emitConstructor();
emitInvoke();
// Additional attributes (none)
asm.emitShort(S0);
// Load class
vec.trim();
final byte[]bytes=vec.getData();
// Note: the class loader is the only thing that really matters
// here -- it's important to get the generated code into the
// same namespace as the target class. Since the generated code
// is privileged anyway, the protection domain probably doesn't
// matter.
return AccessController.doPrivileged(
new PrivilegedAction<MagicAccessorImpl>(){
public MagicAccessorImpl run(){
try{
return(MagicAccessorImpl)
ClassDefiner.defineClass
(generatedName,
bytes,
0,
bytes.length,
declaringClass.getClassLoader()).newInstance();
}catch(InstantiationException|IllegalAccessException e){
throw new InternalError(e);
}
}
});
}
咱們主要看這一句:ClassDefiner.defineClass(xx, declaringClass.getClassLoader()).newInstance();
在ClassDefiner.defineClass方法實現(xiàn)中,每被調(diào)用一次都會生成一個DelegatingClassLoader類加載器對象 绍绘,這里每次都生成新的類加載器奶镶,是為了性能考慮,在某些情況下可以卸載這些生成的類陪拘,因為類的卸載是只有在類加載器可以被回收的情況下才會被回收的厂镇,如果用了原來的類加載器,那可能導(dǎo)致這些新創(chuàng)建的類一直無法被卸載左刽。
而反射生成的類捺信,有時候可能用了就可以卸載了,所以使用其獨立的類加載器欠痴,從而使得更容易控制反射類的生命周期迄靠。
反射調(diào)用流程小結(jié)
最后秒咨,用幾句話總結(jié)反射的實現(xiàn)原理:
反射類及反射方法的獲取,都是通過從列表中搜尋查找匹配的方法掌挚,所以查找性能會隨類的大小方法多少而變化拭荤;
每個類都會有一個與之對應(yīng)的Class實例,從而每個類都可以獲取method反射方法疫诽,并作用到其他實例身上舅世;
反射也是考慮了線程安全的,放心使用奇徒;
反射使用軟引用relectionData緩存class信息雏亚,避免每次重新從jvm獲取帶來的開銷;
反射調(diào)用多次生成新代理Accessor, 而通過字節(jié)碼生存的則考慮了卸載功能摩钙,所以會使用獨立的類加載器罢低;
當找到需要的方法,都會copy一份出來胖笛,而不是使用原來的實例网持,從而保證數(shù)據(jù)隔離;
調(diào)度反射方法长踊,最終是由jvm執(zhí)行invoke0()執(zhí)行
本文使用 文章同步助手 同步