代理
代理模式是一種結構型設計模式,它允許我們通過添加一個代理對象來控制對另一個對象的訪問。代理對象和實際對象具有相同的接口,使得客戶端在不知情的情況下可以使用代理對象進行操作。代理對象在與客戶端進行交互時,可以控制對實際對象的訪問,以實現一些額外的功能,例如訪問計數、延遲加載、權限控制等。
代理分為靜態代理和動態代理兩種
靜態代理
靜態代理是指在編譯期間就已經確定代理類和被代理類的關系。代理類需要手動編寫,并且每個被代理類都需要一個對應的代理類
特點
- 編譯期確定:在編譯代碼時,代理類的代碼就已經確定,之后不會再發生變化。
- 代碼冗余:當有多個被代理類或者被代理類的方法很多時,會產生大量重復的代理代碼。
- 靈活性差:如果被代理類的接口發生變化,代理類也需要相應地修改。
?下邊我們舉一個例子幫助大家進行了解
使用接口進行靜態代理
假設我們有一個叫cai的歌手,他只會進行singing和dance兩件事情,但是當它要開演唱會時,他要進行收錢才開始操作,但是收錢這個操作他只會專心執行sing和dance這個操作或者說他不會首先這個操作。這時就需要它的經紀人來幫助他進行收錢操作(代理)
定義cai歌手
public class Cai implements Singer {@Overridepublic void singing() {System.out.println("cai 唱歌");}@Overridepublic int dance() {System.out.println("cai 跳舞");return 0;}
}
定義cai代理
public class Caidaili implements Singer{private Singer cai = new Cai();@Overridepublic void singing() {System.out.println("先收錢");cai.singing();}@Overridepublic int dance() {System.out.println("先收錢");cai.dance();return 0;}
}
他們都要實現Singer這個接口(代理模式的核心思想是通過代理對象來控制對真實對象的訪問,代理對象和真實對象需要對外提供一致的服務。接口定義了一組方法簽名,實現同一個接口能保證代理對象和被代理對象具有相同的方法,客戶端可以以相同的方式調用它們,從而實現對被代理對象的透明訪問。)
public interface Singer {void singing();int dance();
}
最后使用main方法進行調用
public static void main(String[] args) {Singer singer = new Caidaili();singer.singing();singer.dance();}
此時來了一位新的歌手為wu,他同樣會singing和dance,在假設其自己的方法和代理都實現時,我們只需要修改主代碼中的代理對象即可實現
使用繼承方式進行靜態代理
同樣的使用繼承方式我們也可以實現上述操作
public class Singer {public void dance(){System.out.println("cai 跳舞");}public int singing(){System.out.println("cai 唱歌");return 100;}
}
?使用被代理類繼承代理類
public class SingerSub extends Singer {@Overridepublic void dance() {System.out.println("收錢");super.dance();}@Overridepublic int singing() {System.out.println("收錢");return super.singing();}
}
?最后進行執行
public class Main {public static void main(String[] args) {Singer singer = new SingerSub();singer.singing();singer.dance();}
}
?
但是因為沒有接口的實現,我們每次進行都要使用代理類來承接這個被代理類
結合上邊的例子問題也隨之出現,因為我們在代理類中實例化了被代理類,每一個被代理類都要一個代理類在其內部進行實例化,每出現一個被代理類都要生成一個新的代理類進行代理,極為不方便。
此時,我們就需要一個擁有強大業務能力的“經紀人”(動態代理),來代理所有的被代理對象,不用我們進行頻繁的更換經紀人。
動態代理
從 靜態代理 章節中為我們可知,靜態代理存在著諸多的問題,最主要的問題是靜態代理類需要對被代理類做手動的方法映射。造成這個問題的原因是代理對象是通過硬編碼得到的,是在程序編譯前就已經存在的,所以順著這個思路,我們不難得到一個方向,如何代理對象不是通過硬編碼提前寫好的,而是在程序運行中動態生產的,且生成的代理對象可以對被代理類的方法做自動的映射,那么問題不就解決了嗎?是的,這也就是動態代理的大致解決方案。
JDK代理(基于接口實現)
我們同樣以上邊的cai歌手的例子為例
定義一個Singer的接口
public interface Singer {void dance();int singing();
}
?創建一個cai歌手進行實現它
public class Cai implements Singer {@Overridepublic void dance() {System.out.println("Cai 在跳舞");}@Overridepublic int singing() {System.out.println("Cai 在唱歌");return 0;}
}
最后我們使用JDK代理來進行代理cai類
public static void main(String[] args) {Cai cai = new Cai();//第一個參數:類加載器//第二個參數:代理類需要實現的接口數組//第三個參數:InvocationHandler接口,其中包含了一個invoke方法,該方法會在每次調用代理對象的方法時被觸發Singer o = (Singer) Proxy.newProxyInstance(Cai.class.getClassLoader(), new Class[]{Singer.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("singing".equals(method.getName())){System.out.println("先收錢");}else if("dance".equals(method.getName())){System.out.println("先收錢");}Object invoke = method.invoke(cai, args);//使用反射,將該方法作用到cai對象上return invoke;}});o.singing();}
?當我們想要修改被代理類,我們只需要修改
減少了,代理類的創建?
cglib代理(基于繼承進行實現)
創建一個cai對象
public class Cai {public void dance(){System.out.println("cai 跳舞");}public String singing(){System.out.println("cai 唱歌");return "謝謝";}}
創建代理cai的方法
public class CreateCglibProxy {public static Object getProxy(Object o){Enhancer enhancer = new Enhancer();//生成代理對象enhancer.setSuperclass(o.getClass());//將代理對象設置為被代理對象的子類enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {if("singing".equals(method.getName())){System.out.println("先收錢");}else if("dance".equals(method.getName())){System.out.println("先收錢");}Object invoke = methodProxy.invokeSuper(proxyObject, args);return invoke;}});return enhancer.create();}
}
最后進行執行
public class Main {public static void main(String[] args) {Cai cai = new Cai();Cai proxy = (Cai) CreateCglibProxy.getProxy(cai);proxy.dance();}
}