基礎 | Java的反射與動態代理
關於「反射」請參看Class類詳解(反射)部分。
動態代理作為Java反射的主要應用之一,其在多種JavaEE框架中均有使用,如Spring框架中AOP的實現原理就是動態代理,面試中提到AOP也必定會問 「談談對動態代理的理解?」 相關問題,在此做一個梳理與總結。
談談對動態代理的理解?
動態代理作為設計模式中動態代理模式的一部分,其和靜態代理構成鮮明對比。下面分別對代理模式、靜態代理和動態代理技術點進行詳細介紹與分析。
代理模式
代理模式的主要思想是建立一個代理物件來包裝原始物件,任何對原始物件的呼叫必須通過代理物件,並由代理物件來決定是否以及何時將方法呼叫轉到原始物件上。
說明:關於「設計模式」的詳細內容,將放在後續文章進行學習和分享。
靜態代理
靜態代理的主要特徵是代理類和被代理類都是在編譯期間就確定下來的,一個代理類僅能為一個介面服務,不利於程式的擴充套件。
實現步驟:
- 建立被代理類所實現的介面(包括抽象方法);
- 建立被代理類,實現相應的介面並重寫其抽象方法;
- 建立代理類,實現被代理類所實現的介面,建立構造器並傳入被代理類的物件,在重寫介面的抽象方法中發起對被代理類的呼叫。
示例程式碼:
//1.建立代理類所實現的介面
interface ClothFactory {
void productCloth ();
}
// 2.建立被代理類,並實現相應的介面
class NikeClothFactory implements ClothFactory {
// 重寫抽象方法
public void productCloth() {
System.out.println("Nike-Nike!");
}
}
// 3.建立代理類,實現被代理類所實現的介面
class ProxyFactory implements ClothFactory {
ClothFactory cf;
// 建立構造器,傳入的是被代理類的物件!
public ProxyFactory(ClothFactory cf) {
this.cf = cf;
}
// 重寫抽象方法,發起對被代理物件方法的呼叫
public void productCloth() {
cf.productCloth();
}
}
public class TestStaticProxy {
public static void main(String[] args) {
//1.建立被代理類物件
NikeClothFactory ncf = new NikeClothFactory();
//2.建立代理類物件
ProxyFactory pf = new ProxyFactory(ncf);
//3.執行代理類的方法,但實際發起的是對被代理類中指定方法的呼叫
pf.productCloth();
}
}
在程式碼上看,執行的是代理類的方法,但實際是通過代理類方法發起對被代理類方法的呼叫。
動態代理
動態代理主要是為了解決靜態代理中一個代理類僅能服務一個介面的問題。
動態代理的主要特徵是通過代理類來呼叫其它物件的方法,並且是在程式執行時根據需要動態建立被代理類的代理物件。
實現步驟:
- 建立被代理類所實現的介面(包括抽象方法);
- 建立被代理類,實現相應的介面並重寫其抽象方法;
- 建立一個實現InvocationHandler介面的代理類,重寫其invoke()方法以完成代理的具體操作,同時建立blind()方法一方面完成對被代理類物件的例項化,另一方面通過Proxy的靜態newProxyInstance()方法返回一個動態代理類的物件
示例程式碼:
// 1.被代理類所實現的介面
interface Subject {
void action();
}
// 2.建立被代理類,實現介面並重寫介面的抽象方法
class RealSubject implements Subject {
@Override
public void action() {
System.out.println("被代理類-被代理");
}
}
// 3.建立代理類,實現InvocationHandler介面
class MyInvocationHandler implements InvocationHandler {
Object obj;
// 3.1 重寫invoke方法,完成代理的具體操作
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//method為被代理類的目標方法
Object returnVal = method.invoke(obj, args);//呼叫被代理類的action方法
return returnVal;
}
// 3.2 例項化被代理物件,返回一個代理類物件
public Object blind(Object obj) {
this.obj = obj;
// 返回一個代理類物件
return
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
}
public class TestProxy {
public static void main(String[] args) {
//1.建立被代理類的物件
RealSubject real = new RealSubject();
//2.建立代理類的物件
MyInvocationHandler handler = new MyInvocationHandler();
//3.呼叫blind()方法,動態返回一個同樣實現被代理類所實現介面的代理類的物件
Object obj = handler.blind(real);
Subject sub = (Subject)obj;//此時的sub即為代理類的物件
sub.action();
NikeClothFactory ncf = new NikeClothFactory();
obj = handler.blind(ncf);
ClothFactory cf = (ClothFactory)obj; //此時的cf即為代理類的物件
cf.productCloth();
}
}
擴充套件面試題
問:你在專案中的哪些場景下使用過動態代理?
答:動態代理最主要的應用場景就是面向切面程式設計,主要在呼叫目標方法的前後插入一些通用處理,比如日誌操作和事務處理等,示意圖如下:
擴充套件:動態代理之Proxy類
Proxy類是專門完成代理的操作類,其是所有動態代理類的父類,通過此類為一個或多個介面動態地生成實現類。其提供的主要靜態方法如下:
// 獲取動態代理類所對應的Class物件
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
// 建立動態代理物件
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
推薦閱讀
歡迎關注
Java名企面試吧,每天10點24分,我們不見不散!
丙子先生的宗旨是,每天以短篇幅講高頻面試題,不增加太多負擔,但需要持之以恆。
能力有限,歡迎指教!