總覽
這篇文章我們簡(jiǎn)單講一講Java語(yǔ)言中代理模式的幾種實(shí)現(xiàn)方式。這篇文章僅僅是在代碼上的實(shí)現(xiàn)。原理篇會(huì)在后面分開(kāi)來(lái)闡述宿百。
什么是代理模式
要用Java實(shí)現(xiàn)代理工三,首先要知道什么是代理。
下面是在百度百科上關(guān)于代理模式的定義:
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)兑巾。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象,而代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用坠宴。
怎么使用代理模式
具體在Java里的實(shí)現(xiàn),我們?cè)谙旅嫒齻€(gè)模塊代碼中來(lái)闡述绷旗。這里就簡(jiǎn)單介紹一下一個(gè)代理模式的組成喜鼓,它在一定程度上表現(xiàn)了代理模式的基本使用副砍。
抽象角色:通過(guò)接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法。
代理角色:實(shí)現(xiàn)抽象角色庄岖,是真實(shí)角色的代理豁翎,通過(guò)真實(shí)角色的業(yè)務(wù)邏輯方法來(lái)實(shí)現(xiàn)抽象方法,并可以附加自己的操作隅忿。
真實(shí)角色:實(shí)現(xiàn)抽象角色谨垃,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯,供代理角色調(diào)用硼控。
使用代理模式的好處
-
職責(zé)清晰
真實(shí)的角色就是實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯刘陶,不用關(guān)心其他非本職責(zé)的事務(wù),通過(guò)后期的代理完成一件完成事務(wù)牢撼,附帶的結(jié)果就是編程簡(jiǎn)潔清晰匙隔。 - 代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用,這樣起到了中介的作用和保護(hù)了目標(biāo)對(duì)象的作用熏版。
- 高擴(kuò)展性
- 降低了耦合
在代理類中纷责,我們可以添加自己的動(dòng)作,不在真實(shí)角色業(yè)務(wù)邏輯中的操作我們可以在代理類中進(jìn)行添加撼短。保證了業(yè)務(wù)邏輯的正確性再膳。用一個(gè)爛大街的例子。現(xiàn)在我們有四個(gè)角色曲横,一個(gè)是房東喂柒,一個(gè)是房子,一個(gè)是租客禾嫉,一個(gè)是中介灾杰。處理租客,其他三個(gè)對(duì)應(yīng)的代理模式中的角色想必非常清楚熙参。如果租客和房東直接溝通艳吠,也就是看房->付定金->入住這樣一個(gè)邏輯,但是看房和入住跟房東都沒(méi)有太大的直接關(guān)系孽椰。這個(gè)時(shí)候昭娩,如果看房、入住都讓房東親自來(lái)處理黍匾,那我們的房東必然會(huì)忙的不可開(kāi)交栏渺,并且不利于我們可愛(ài)的房東從復(fù)雜的租房活動(dòng)中清閑起來(lái),這個(gè)時(shí)候膀捷,我們使用代理模式迈嘹,也就是說(shuō)房東找一個(gè)中介公司削彬,把自己提供租房這個(gè)服務(wù)交給中介來(lái)代理全庸,讓他們完成租房的工作秀仲。我們的房東只要負(fù)責(zé)收錢就好,看房啊壶笼、入住辦理啊神僵、這種事情就交給中介(代理)去處理就好。這樣覆劈,我們實(shí)現(xiàn)了保礼,職責(zé)清晰、保護(hù)了目標(biāo)對(duì)象责语、高擴(kuò)展度炮障、降低了耦合的優(yōu)良形態(tài)。
靜態(tài)代理
靜態(tài)代理的實(shí)現(xiàn)
到這里已經(jīng)讀了挺多字了坤候。這里我們上幾段代碼來(lái)簡(jiǎn)單講Java中靜態(tài)代理的實(shí)現(xiàn)胁赢。
首先是一個(gè)接口。也就是所謂的接口角色白筹。我們通過(guò)接口角色聲明真實(shí)角色的業(yè)務(wù)智末。
public interface Hello {
void say(String name);
}
然后是一個(gè)實(shí)現(xiàn)類。也就是所謂的真實(shí)角色徒河,在這個(gè)類中系馆,我們定義所要實(shí)現(xiàn)的業(yè)務(wù)邏輯。
public class HelloImp implements Hello{
@Override
public void say(String name) {
System.out.println("Hello! " + name);
}
}
最后是一個(gè)代理類顽照。我們使用代理類來(lái)代理真實(shí)類由蘑,通過(guò)真實(shí)類對(duì)應(yīng)方法中的業(yè)務(wù)邏輯來(lái)實(shí)現(xiàn)接口中的抽象方法。特別的是代兵,我們可以在代理類中添加自己的操作纵穿。
動(dòng)態(tài)代理
靜態(tài)代理未免有點(diǎn)太死板了、這么直接固定的代理方式奢人,會(huì)導(dǎo)致我們每有一個(gè)新的Hello接口的實(shí)現(xiàn)谓媒,就需要編寫(xiě)一個(gè)新的代理類。這未免也有點(diǎn)太臃腫了何乎。Java待我們很好句惯,給我提供了兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式,一種是JDK動(dòng)態(tài)代理支救,一種是CGLib動(dòng)態(tài)代理抢野。JDK動(dòng)態(tài)代理是Java原生的,而CGLib則是一個(gè)開(kāi)源項(xiàng)目各墨。(點(diǎn)擊下載CGLib jar包)
jdk動(dòng)態(tài)代理
我們首先來(lái)講Jdk動(dòng)態(tài)代理的實(shí)現(xiàn)指孤,然后通過(guò)和CGLib的不同引入CGLib動(dòng)態(tài)代理。老套路,直接粘貼代碼恃轩。
動(dòng)態(tài)代理代碼:
package com.blaze.study.aop.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyDemo06 implements InvocationHandler{
private Object target;
public DynamicProxyDemo06(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
operator1();
Object result = method.invoke(target,args);
operator2();
return result;
}
public void operator1(){
System.out.println("添加的操作1");
}
public void operator2(){
System.out.println("添加的操作2");
}
}
這里我們通過(guò)實(shí)現(xiàn)InvocationHandler接口结洼,并且提供其invoke方法的實(shí)現(xiàn),就可以為所欲為叉跛,動(dòng)態(tài)代理任何我們想要代理的類松忍。
接下面,通過(guò)一段使用者來(lái)闡述一些動(dòng)態(tài)代理代碼上的優(yōu)化筷厘。
package com.blaze.study.aop.dynamic;
import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;
import java.lang.reflect.Proxy;
public class DynamicProxyTestDemo06 {
public static void main(String[] args) {
Hello h = new HelloImp();
DynamicProxyDemo06 dy = new DynamicProxyDemo06(h);
Hello hello = (Hello)Proxy.newProxyInstance(h.getClass().getClassLoader(),
h.getClass().getInterfaces(),
dy);
hello.say("Sherlock");
}
}
我們通過(guò)Proxy.newProxyInstance來(lái)生成Hello的引用并且使用通過(guò)類型轉(zhuǎn)換最后來(lái)調(diào)用say方法鸣峭。
根據(jù)在使用層面代碼越簡(jiǎn)單越好的原則,我們通過(guò)可以通過(guò)在DynamicProxyDemo06類中添加這樣的方法來(lái)改造我們的動(dòng)態(tài)代理類酥艳。
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
我們通過(guò)范型來(lái)完成類型的匹配摊溶,而不需要在調(diào)用者中使用強(qiáng)制方式的來(lái)進(jìn)行類型轉(zhuǎn)換。
這個(gè)時(shí)候充石,我們就可以在調(diào)用者中通過(guò)下面的簡(jiǎn)單代碼來(lái)調(diào)用被調(diào)用的方法更扁。
package com.blaze.study.aop.dynamic;
import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;
public class DynamicProxyTestDemo06 {
public static void main(String[] args) {
Hello h = new DynamicProxyDemo06(new HelloImp()).getProxy();
h.say("Sherlock");
}
}
動(dòng)態(tài)代理跟靜態(tài)代理的區(qū)別就是在動(dòng)態(tài)代理中我們不需要為每一個(gè)類寫(xiě)一個(gè)代理類。我們僅僅通過(guò)DynamicProxyDemo06這一個(gè)類來(lái)代理我們想要代理的任何類赫冬。比如我們有一個(gè)World接口浓镜、和World實(shí)現(xiàn)類。
World.java
package com.blaze.study.aop.proxy;
public interface World {
void say(String word);
}
WorldImpl.java
package com.blaze.study.aop.proxy;
public class WorldImpl implements World{
@Override
public void say(String word) {
System.out.println(word + "\tWorld!");
}
}
這樣我們通過(guò)代碼調(diào)用代理的服務(wù)劲厌,并且添加我們的操作膛薛,簡(jiǎn)單輕松。
package com.blaze.study.aop.dynamic;
import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;
import com.blaze.study.aop.proxy.World;
import com.blaze.study.aop.proxy.WorldImpl;
public class DynamicProxyTestDemo06 {
public static void main(String[] args) {
Hello h = new DynamicProxyDemo06(new HelloImp()).getProxy();
h.say("Sherlock");
/**新的被代理類*/
World w = new DynamicProxyDemo06(new WorldImpl()).getProxy();
w.say("Blaze");
}
}
CGLib動(dòng)態(tài)代理
接下面要講的動(dòng)態(tài)代理實(shí)現(xiàn)的方式就是CGLib补鼻,CGLib博大精深哄啄,這篇文章里我們就完成一個(gè)簡(jiǎn)單的實(shí)現(xiàn)。JDK動(dòng)態(tài)代理必須要我們的被代理類有一個(gè)接口风范,如果沒(méi)有接口咨跌,就會(huì)報(bào)類型轉(zhuǎn)化錯(cuò)誤。
我們僅簡(jiǎn)單的刪除掉了WorldImpl類實(shí)現(xiàn)的接口硼婿。就爆出這樣嚴(yán)重的錯(cuò)誤锌半。假如我們不想要實(shí)現(xiàn)任何接口呢,不想用接口來(lái)描述所要代理的方法寇漫。
這個(gè)時(shí)候刊殉,我們最好使用CGLib動(dòng)態(tài)代理,它可以代理沒(méi)有任何接口的類州胳。接下來(lái)我們就來(lái)寫(xiě)這樣的一個(gè)動(dòng)態(tài)代理记焊。
代理類 --> CGLibProxyDemo05
package com.blaze.study.aop.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibProxyDemo05 implements MethodInterceptor{
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> cls){
return (T) Enhancer.create(cls,this);
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)throws Throwable{
operator1();
Object result = proxy.invokeSuper(target,args);
operator2();
return result;
}
public void operator1(){
System.out.println("Operator1");
}
public void operator2(){
System.out.println("Operator2");
}
}
我們通過(guò)提供的接口,調(diào)用invokeSuper方法栓撞,來(lái)完成CGLib的動(dòng)態(tài)代理遍膜。我們通過(guò)這樣的方式來(lái)調(diào)用服務(wù)碗硬。
使用者 --> CGLibProxyTestDemo05
package com.blaze.study.aop.cglib;
import com.blaze.study.aop.proxy.Hello;
import com.blaze.study.aop.proxy.HelloImp;
import com.blaze.study.aop.proxy.WorldImpl;
public class CGLibProxyTestDemo05 {
public static void main(String[] args) {
Hello h = new CGLibProxyDemo05().getProxy(HelloImp.class);
h.say("Sherlock");
WorldImpl w = new CGLibProxyDemo05().getProxy(WorldImpl.class);
w.say("Blaze");
}
}
結(jié)果
在上面的代碼中,我們已WorldImpl實(shí)現(xiàn)接口的聲明刪除掉了瓢颅。通過(guò)程序執(zhí)行的結(jié)果恩尾,我們可以清楚的看到CGLib動(dòng)態(tài)代理不僅可以代理動(dòng)態(tài)的有接口的HelloImp類,也可以代理沒(méi)有實(shí)現(xiàn)任何接口的WorldImpl類惜索。這是比JDk動(dòng)態(tài)代理進(jìn)步的地方。而關(guān)于這一塊的原理剃浇,我們也會(huì)用相應(yīng)的幾篇博客一起探索其中的原理巾兆。Flag立在這里。
總結(jié)
通過(guò)上面文字的編寫(xiě)虎囚,我已經(jīng)沒(méi)有力氣寫(xiě)總結(jié)了角塑。所以大家自己總結(jié)吧。謝謝大家的支持淘讥。歡迎訪問(wèn)我的個(gè)人博客圃伶。