什麼是代理模式
代理模式就是為一個對象提供一個代理對象,由這個代理對象控制對該對象的訪問。
理解代理模式,可以對照生活中的一些具體例子,比如房產中介、二手車交易市場、經紀人等。
為什麼要用代理模式
通過使用代理模式,我們避免了直接訪問目標對象時可能帶來的一些問題,比如:遠程調用,需要使用遠程代理來幫我們處理一些網絡傳輸相關的細節邏輯;可能需要基於某種權限控制對目標資源的訪問,可以使用保護代理等。
總的來說,通過是用代理模式,我們可以控制對目標對象的訪問,可以在真實方法被調用前或調用后,通過代理對象加入額外的處理邏輯。
代理模式分類
代理模式分為靜態代理和動態代理。動態代理根據實現不同又可細分為JDK動態代理和cglib動態代理。
靜態代理是由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就確定了。
動態代理是在實現階段不用關心代理類,而在運行時動態生成代理類的。
靜態代理
以房哥買房子為例,用代碼實現靜態代理。
1、首先建立一個Seller接口
public interface Seller {
void sell();
}
2、創建實現類,房哥,有一個方法,就是買房子
public class FangGe implements Seller{
@Override
public void sell() {
System.out.println("房哥要出手一套四合院");
}
}
3、買房子需要找到買家,達成交易后還要辦理過戶等其他手續,房哥只想賣房收錢就完了。因此,需要找一個代理來幫房哥處理這些雜事。
我們創建一個代理類FangGeProxy,代理類也需要實現Seller接口,行為上要保持和FangGe一樣,都是要賣房子。同時該代理類還需要持有房哥的引用。
public class FangGeProxy implements Seller{
private FangGe fangGe;
public FangGeProxy(FangGe fangGe){
this.fangGe = fangGe;
}
@Override
public void sell() {
findBuyer();
fangGe.sell();
afterSell();
}
public void findBuyer(){
System.out.println("代理幫助尋找買主");
}
public void afterSell(){
System.out.println("達成交易后,辦理相關手續");
}
}
可以看到,房哥的代理類通過findBuyer()和afterSell()兩個方法幫助房哥完成了其他一些雜事。
4、測試類
public class StaticProxyTest {
public static void main(String[] args) {
Seller seller = new FangGeProxy(new FangGe());
seller.sell();
}
}
輸出:
代理幫助尋找買主
房哥要出手一套四合院
達成交易后,辦理相關手續
最後,看下類圖
靜態代理的問題:
1、由於靜態代理類在編譯前已經確定了代理的對象,因此靜態代理只能代理一種類型的類,如果要給大量的類做代理,就需要編寫大量的代理類;
2、如果我們要給Seller,也就是目標對象要增加一些方法,則需要同步修改代理類,不符合開閉原則。
JDK動態代理
JDK的動態代理依賴於jdk給我們提供的類庫實現,是一種基於接口實現的動態代理,在編譯時並不知道要代理哪個類,而是在運行時動態生成代理類。同時也解決了靜態代理中存在的問題。
我們接上上面靜態代理的例子,繼續實現JDK的動態代理。
1、我們建一個方法轉發的處理器類,該類需要實現InvocationHandler接口。
public class SellerInvocationHandler implements InvocationHandler {
// 要代理的真實對象
private Object target;
/**
* 使用Proxy類靜態方法獲取代理類實例
*/
public Object getProxyInstance(Object target){
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target, args);
after();
return obj;
}
private void before() {
System.out.println("執行方法前");
}
private void after() {
System.out.println("執行方法后");
}
}
2、新建JDK動態代理測試類,首先代理房哥賣房子
public class JDKDynamicProxyTest {
public static void main(String[] args) {
// new一個房哥,下面幫房哥找個代理
FangGe fangGe = new FangGe();
SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
// 房哥的代理對象
Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe);
seller.sell();
}
}
輸出:
執行方法前
房哥要出手一套四合院
執行方法后
可以看到,完成了代理。
3、接下來我們新建另外一個類,User類,並使用JDK動態代理完成代理User類
public interface IUser {
void sayHello();
void work();
}
public class UserImpl implements IUser{
@Override
public void sayHello() {
System.out.println("hello,我是小明");
}
@Override
public void work() {
System.out.println("我正在寫代碼");
}
}
修改測試類,
public class JDKDynamicProxyTest {
public static void main(String[] args) {
/* // new一個房哥,下面幫房哥找個代理
FangGe fangGe = new FangGe();
SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
// 房哥的代理對象
Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe);
seller.sell();*/
// 代理user類
IUser user = new UserImpl();
SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
IUser userProxy = (IUser) sellerInvocationHandler.getProxyInstance(user);
userProxy.sayHello();
userProxy.work();
}
}
輸出:
執行方法前
hello,我是小明
執行方法后
執行方法前
我正在寫代碼
執行方法后
可以看到,我們SellerInvocationHandler 並未做任何改動,它便能為UserImpl類生成代理,並在執行方法的前後增加額外的執行邏輯。
cglib動態代理
JDK動態代理有一個局限就是,被代理的類必須要實現接口。如果被代理的類沒有實現接口,則JDK動態代理就無能為力了。這個時候該cglib動態代理上場了。
CGLIB是一個功能強大,高性能的代碼生成包。它為沒有實現接口的類提供代理,為JDK的動態代理提供了很好的補充。通常可以使用Java的動態代理創建代理,但當要代理的類沒有實現接口或者為了更好的性能,CGLIB是一個好的選擇。
1、新建一個MyCglibInterceptor,實現MethodInterceptor接口。該類類似於JDK動態代理中的InvocationHandler實例,是實現cglib動態代理的主要類。
public class MyCglibInterceptor implements MethodInterceptor {
public Object getCglibProxyInstance(Object object){
// 相當於Proxy,創建代理的工具類
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("執行方法之前");
}
private void after() {
System.out.println("執行方法之後");
}
}
2、新建cglib動態代理的測試類,先代理上面例子中的User類。
public class CglibDynamicProxyTest {
public static void main(String[] args) {
MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl());
userCglibProxy.sayHello();
userCglibProxy.work();
}
}
輸出:
執行方法之前
hello,我是小明
執行方法之後
執行方法之前
我正在寫代碼
執行方法之後
3、新建一個類HelloWorld,不實現任何接口,為該類實現動態代理。
public class HelloWorld {
public void hello(){
System.out.println("世界這麼大,我想去看看");
}
}
測試代理類
public class CglibDynamicProxyTest {
public static void main(String[] args) {
/* MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl());
userCglibProxy.sayHello();
userCglibProxy.work();*/
// 代理未實現任何接口的類
MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
HelloWorld helloWorldProxy = (HelloWorld) myCglibInterceptor.getCglibProxyInstance(new HelloWorld());
helloWorldProxy.hello();
}
}
輸出:
執行方法之前
世界這麼大,我想去看看
執行方法之後
使用cglib動態代理,我們實現了對普通類的代理。
(完)
設計模式系列文章
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】
※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計
※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務
※Google地圖已可更新顯示潭子電動車充電站設置地點!!