我想在網上查了一些有關 Java 動態代理相關的技術資料,發現講的都是一些理論或者源碼,沒有太多實際的應用舉例,讓人看的雲里霧里、似懂非懂。索性我就自己總結一下,方便后續在使用時進行快速查閱。
Java 動態代理技術其實是 AOP 編程思想的實現。AOP 編程思想可以簡單的理解為:在不改變原有業務代碼情況下,實現對原有業務功能的修改或擴展。
AOP 編程思想在我們的實際開發中經常使用,比如你開發了一個網站系統,某些功能需要有權限才能使用,你肯定不想在每個具體的方法上進行硬編碼實現權限驗證,這樣很不好統一維護,你會編寫一個權限過濾器,對用戶的請求進行攔截,驗證用戶權限。這里的權限過濾器其實就是 AOP 編程思想的一個體現,在不改變原有業務功能代碼的情況下,實現對原有業務功能的權限驗證擴展。
Java 動態代理技術 AOP 編程思想的實現方式,本質上就是利用反射技術,為實現了【具體接口】的實現類對象,創建實現了【具體接口】的代理對象,通過對代理對象的 invoke 方法進行代碼修改,從而在不改變原有實現類代碼的前提下,對原有實現類實例化后的對象的相關方法進行功能修改或者擴展。
從上面的描述可以發現:要實現 Java 動態代理技術,首先必須要有一個接口,以及該接口的實現類。下面我們還是舉一個簡單的例子來詳細介紹 Java 動態代理技術吧。
// 定義一個接口
public interface PersonInterface {
//上午
void Morning();
//下午
void Afternoon();
//傍晚
void Evening();
//夜里
void Night();
}
// 定義 Person 類,實現了 PersonInterface 接口中的方法
public class Person implements PersonInterface {
@Override
public void Morning() {
System.out.println("上午:讀書學習...");
}
@Override
public void Afternoon() {
System.out.println("下午:鍛煉身體...");
}
@Override
public void Evening() {
System.out.println("傍晚:聽歌娛樂...");
}
@Override
public void Night() {
System.out.println("夜里:早點睡覺...");
}
}
現在我們要實現目標是:在不改變 Person 類中任何源代碼的情況下,對 Person 類實例化后的對象中 Morning、Afternoon、Evening 這 3 個方法,進行功能增強或者修改,調用這 3 個方法打印的內容是修改后的內容,不是原有內容。
注意:要求必須使用 Person 類進行實例化對象,然后調用 Morning、Afternoon、Evening 這 3 個方法。
這就導致我們無論是采用一個新的類,去實現 PersonInterface 接口,重新實現 Morning、Afternoon、Evening 這 3 個方法,還是采用一個新的類,繼承 Person 去重寫 Morning、Afternoon、Evening 這 3 個方法,都是不可行的。因為最終我們實例化 Person 對象后,打印的還是 Person 中這 3 個方法的原有內容。
這里只能采用 Java 動態代理技術來實現,具體代碼如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//實例化一個 Person 對象 wolfer
Person wolfer = new Person();
/*
基於 wolfer 對象和 PersonInterface 接口,創建動態代理對象
*/
//第一種方式:采用匿名內部類實現
PersonInterface wolferProxy = (PersonInterface) Proxy.newProxyInstance(
//wolfer 對象的類加載器,其實就是 Person 類的加載器
wolfer.getClass().getClassLoader(),
//wolfer 對象所實現的接口的字節碼,其實就是 Person 類所實現的接口的字節碼
wolfer.getClass().getInterfaces(),
//動態代理的具體實現(匿名內部類實現方式)
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
try {
if ("Morning".equals(method.getName())) {
//如果是 wolfer 對象的 Morning 方法
//給 wolfer 對象的原有 Morning 方法代碼前,增加一行代碼
System.out.println("上午:睡到自然醒,然后起床...");
//調用 Morning 的原有方法
return method.invoke(wolfer, args);
} else if ("Afternoon".equals(method.getName())) {
//調用 Afternoon 的原有方法,獲取返回值
Object obj = method.invoke(wolfer, args);
//如果是 wolfer 對象的 Afternoon 方法
//給 wolfer 對象的原有 Afternoon 方法代碼后,增加一行代碼
System.out.println("下午:去吃個大餐,補充能量...");
return obj;
} else if ("Evening".equals(method.getName())) {
//如果是 wolfer 對象的 Evening 方法
//直接改變了 Evening 原有方法的實現代碼
System.out.println("傍晚:燈紅酒綠,紙醉金迷...");
//由於 Evening 方法不需要返回值,因此這里返回 null 即可
return null;
} else {
//其它方法不進行改變和擴展,直接調用原有方法
return method.invoke(wolfer, args);
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
});
//第二種實現方式:
//由於動態代理的第 3 個參數 InvocationHandler 是僅有一個方法的接口,
//因此可以簡化為 Lambda 表達式的編寫方式
PersonInterface wolferProxy = (PersonInterface) Proxy.newProxyInstance(
wolfer.getClass().getClassLoader(),
wolfer.getClass().getInterfaces(),
(proxy, method, proxyArgs) -> {
try {
if ("Morning".equals(method.getName())) {
System.out.println("上午:睡到自然醒,然后起床...");
return method.invoke(wolfer, proxyArgs);
} else if ("Afternoon".equals(method.getName())) {
Object obj = method.invoke(wolfer, proxyArgs);
System.out.println("下午:去吃個大餐,補充能量...");
return obj;
} else if ("Evening".equals(method.getName())) {
System.out.println("傍晚:燈紅酒綠,紙醉金迷...");
return null;
} else {
return method.invoke(wolfer, proxyArgs);
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
);
//上面兩種實現方式,只保留一種即可
//調用 wolfer 的動態代理對象的 Morning 方法,發現:
//相比 Person 類的原有 Morning 方法,前面增加了一句打印
wolferProxy.Morning();
//調用 wolfer 的動態代理對象的 Afternoon 方法,發現:
//相比 Person 類的原有 Afternoon 方法,發現后面增加了一句打印
wolferProxy.Afternoon();
//調用 wolfer 的動態代理對象的 Evening 方法,發現:
//相比 Person 類的原有 Evening 方法,打印的內容被徹底改變了
wolferProxy.Evening();
//調用 wolfer 的動態代理對象的 Night 方法
//由於動態代理沒有對其修改和擴展,因此打印內容跟 Person 類原有的 Night 方法相同
wolferProxy.Night();
}
}
/*
打印內容如下所示:
上午:睡到自然醒,然后起床...
上午:讀書學習...
下午:鍛煉身體...
下午:去吃個大餐,補充能量...
傍晚:燈紅酒綠,紙醉金迷...
夜里:早點睡覺...
*/
Ok,通過以上例子,應該能夠快速理解並掌握 Java 動態代理技術了。
實際應用場景是:如果你引用了第三方的 jar 包,如果你感覺它的某些類的方法不太滿足自身的需求,想要改變或增強的話,而這些類恰好也實現了某些接口,此時就可以使用 Java 動態代理技術就行修改或增強,來滿足自身業務的需要。另外在 Java 的各種框架中,比如 Spring 相關框架也廣泛使用 Java 動態代理技術。
希望以上內容對大家有用。