可進入本人語雀文檔看,格式更清晰明了哦
https://www.yuque.com/docs/share/3c013ec6-6c35-4854-aaf6-ff9a6e8a6af2?# 《通俗易懂講反射》
文章架構

反射是個啥
反射就是在運行期,可以獲取任意一個對象的屬性和方法,可以調用任意一個對象的方法和屬性。是一種動態獲取對象信息以及動態調用對象的方法。最常見的場景就是在動態代理。而動態代理應用最廣的地方就是各種框架,比如:Spring
Class 對象
了解反射必定離不開 Class 對象。
爪窪國的人都知道,代碼編寫完后是要編譯成 .class 文件的。這里的一個個 .class 文件,被虛擬機加載到內存后就是以 Class 對象存在,一個類只能有一個 .class 文件,因此,一個類也就對應一個 Class 對象,Class 對象是類的描述信息,比如:字段、方法、注釋等等等等,只要你想得到的類結構,他在 Class 對象內都有體現,並且都能通過 Class 對象獲取到。
如何獲取 Class 對象引用
有以下兩種方式獲取:
- 使用 Class 類的一個靜態方法 forName() 獲取:Class<?> clazz = Class.forName("className");
- 通過類字面常量獲取:Class
= String.class;
Class 類常用方法
這里偷個懶,進入 class 文件,通過 idea alt + 7 快捷鍵就可以展示類擁有的字段、方法。
通過方法名其實就大概能猜出這個方法是干嘛的了。
比如:
這里的例子可能有點繞,就隨便拿一個類來舉例:比如 String 類。
String 類有一個描述其結構的 Class 對象,下面用 Class
-
newInstance():通過 Class
對象實例化一個 String 對象。 -
getName():獲取 String 對象的類名
-
getInterfaces():獲取 String 對象所實現的所有接口信息
-
getMethods():獲取 String 對象所有的方法信息

代理
既然反射應用最廣的地方就是動態代理,那么只介紹如何在運行中獲取一個類的結構就有點說不過去了。好歹也得介紹下動態代理吧。
動態代理
動態代理一共需要三個東西:
-
接口,以下的代碼是 Interface 接口
-
實現接口的類,也是要被代理的類,以下的代碼是 Clazz 類
-
實現 InvocationHandler 接口的代理類,以下的代碼是 ProxyHandler 類。
代理類就是通過現有的字節碼,通過增強的方式對要調用的方法進行增強,最后動態生成新的字節碼文件,並且生成新的 .class 文件,最后通過反射的方式執行增強的方法。
如下,ProxyHandler 類中的 invoke 方法就是增強后的方法,這里在原來的被代理類執行前和執行后分別打印了兩行字符串,實際應用場景可能是事務的開啟和提交。
在 DynamicProxyDemo 類的 main 方法里面,通過 Proxy 類的靜態方法 newProxyInstance() 生成一個被增強后的代理類,newProxyInstance() 方法需要三個入參
-
類加載器,隨便找個已經被加載的類獲取其類加載器即可
-
Class 數組,被代理類所實現的接口數組,由此可見,jdk 的動態代理必須實現接口
-
實現 InvocationHandler 接口的代理類對象,必須把被代理的類傳入代理類中
獲取到增強后的代理類后,將其強轉成被代理類對象所實現的接口,然后調用方法,就可以發現,除了原本的實現,增強的邏輯也執行了。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Interface {
void m1();
String m2();
String m3(String a);
}
class Clazz implements Interface {
@Override
public void m1() {
System.out.println("Clazz 的 m1() 方法");
}
@Override
public String m2() {
System.out.println("Clazz 的 m2() 方法");
return "";
}
@Override
public String m3(String a) {
System.out.println("Clazz 的 m3() 方法");
return a;
}
}
class ProxyHandler implements InvocationHandler {
private Object obj;
public ProxyHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("------ method:%s start proxy ------%n", method.getName());
Object invoke = method.invoke(obj, args);
System.out.printf("------ method:%s end proxy ------%n%n", method.getName());
return invoke;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
Interface proxy = (Interface) Proxy.newProxyInstance(DynamicroxyDemo.class.getClassLoader(), new Class[]{Interface.class}, new ProxyHandler(new Clazz()));
proxy.m1();
proxy.m2();
proxy.m3("Clazz 的 m3() 方法");
}
}
運行結果:
------ method:m1 start proxy ------
Clazz 的 m1() 方法
------ method:m1 end proxy ------
------ method:m2 start proxy ------
Clazz 的 m2() 方法
------ method:m2 end proxy ------
------ method:m3 start proxy ------
Clazz 的 m3() 方法
------ method:m3 end proxy ------
靜態代理
既然都講了動態代理,那就來講講靜態代理吧。
動態代理是在運行時動態生成字節碼,然后執行增強方法。
靜態代理也需要三個東西:
-
接口
-
實現接口的被代理類
-
實現接口的代理類
其中被代理類實現接口后,實現了自己的代碼邏輯。
代理類也實現了相同的接口,然后在對應的方法里調用被代理類的方法就行了,需要增強方法的話在調用方法前或者調用方法后編寫增強代碼即可,所以代理類需要持有一個被代理類的引用。代碼十分簡單!看下面的例子。
interface StaticProxyInterface {
void m1();
String m2();
String m3(String a);
}
class StaticProxyClazz implements StaticProxyInterface {
@Override
public void m1() {
System.out.println("Clazz 的 m1() 方法");
}
@Override
public String m2() {
return "Clazz 的 m2() 方法";
}
@Override
public String m3(String a) {
return a;
}
}
class FinalProxyClazz implements StaticProxyInterface {
private StaticProxyInterface obj;
public FinalProxyClazz(StaticProxyInterface obj) {
this.obj = obj;
}
@Override
public void m1() {
System.out.println("------ start proxy ----");
obj.m1();
}
@Override
public String m2() {
System.out.println("------ start proxy ----");
return obj.m2();
}
@Override
public String m3(String a) {
System.out.println("------ start proxy ----");
return obj.m3(a);
}
}
public class StaticProxyDemo {
public static void main(String[] args) {
FinalProxyClazz finalProxyClazz = new FinalProxyClazz( new StaticProxyClazz());
finalProxyClazz.m1();
System.out.println(finalProxyClazz.m2());
System.out.println(finalProxyClazz.m3("Clazz 的 m3() 方法"));
}
}
運行結果:
------ start proxy ----
Clazz 的 m1() 方法
------ start proxy ----
Clazz 的 m2() 方法
------ start proxy ----
Clazz 的 m3() 方法
文章為本人學習過程中的一些個人見解,漏洞是必不可少的,希望各位大佬多多指教,幫忙修復修復漏洞!
