其實生活中代理模式很常見,比如房產中介,明星經紀,就連追女孩都體現了代理模式。
下面引用《大話設計模式》中的一張圖
上圖最後一句也說明了爲什麼代理類和被代理類都要實現同一個接口,利用了接口可以引用不同子類的特性,從而可以在任何使用目標對象(被代理的那個)的地方使用代理對象來增強方法。
代碼實例
假如現在這樣一個場景,男孩(追求者)喜歡一個女孩,想送個禮物給女孩,但是男孩不好意思去,所以得找個中間人幫忙送一下。
追求者送禮物的動作接口
public interface Action {
/**
* 送女生禮物
*/
public void givePresent();
}
追求者類
/**
* 追求者類
* @author 陽光大男孩!!!
*/
public class Seeker implements Action {
@Override
public void givePresent() {
System.out.println("追求者送禮物");
}
}
中間人-代理對象類
/**
* @author 陽光大男孩!!!
* Seeker類的代理對象類
*/
public class Proxy implements Action{
Seeker seeker = null;
@Override
public void givePresent() {
if(seeker==null)
{
seeker = new Seeker();
}
seeker.givePresent();
}
}
中間人擁有追求者對象,從而可以在不修改追求者類代碼的基礎上增強方法。
被追求的女孩類
/**
* 被追求者類
* @author 陽光大男孩!!!
*/
public class Girl {
}
主函數測試類
/**
* @author 陽光大男孩!!!
*/
public class Main {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.givePresent();
}
}
執行結果
追求者送禮物
前面講的叫做靜態代理
,缺點就是,假如在項目中需要代理模式,那每增強一個對象,都要寫一個代理類,所以很麻煩,下面引入一個動態代理的概念,從而能夠動態的創建代理類,不需要我們自己寫了。
動態代理
使用動態代理的話,需要寫一個工廠類來給我們自動創建代理類。在java中,有兩種方式來實現動態代理,分別是JDK方式和Cglib方式。
區別在於:JDK的Proxy方式實現的動態代理 目標對象必須有接口 沒有接口不能實現jdk版動態代理
1 JDK方式
/**
* @author 陽光大男孩!!!
*/
public class ProxyFactory {
public static <T> Object getProxy(T t){
//返回一個代理對象
Object object = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("執行"+method.getName()+"方法前...");
Object invoke = method.invoke(t, args);
System.out.println("執行"+method.getName()+"方法後...");
return invoke;
}
});
return object;
}
}
實現代理工廠類後,就不需要專門爲某個對象寫代理類了,其他類不變
/**
* @author 陽光大男孩!!!
*/
public class Main {
public static void main(String[] args) {
Seeker seeker = new Seeker();
Action action = (Action) ProxyFactory.getProxy(seeker);
action.givePresent();
}
}
執行結果:
執行givePresent方法前...
追求者送禮物
執行givePresent方法後...
2.CGLIB方式
使用CGLIB方式,需要引入CGLIB包,這裏我選擇使用maven幫我們引入,所以要將我們之前的普通項目轉換爲maven項目
在pom.xml中加入依賴
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
再進行import changes操作
如果是在spring boot 項目中使用,CGLIB包已經被集成,直接使用即可。
實現代理工廠類
/**
* @author 陽光大男孩!!!
*/
public class CglibFactory {
public static <T> Object getProxy(T t){
//幫我們生成代理對象
Enhancer en = new Enhancer();
//設置要代理的目標類
en.setSuperclass(t.getClass());
//代理要做什麼
en.setCallback(new MethodInterceptor() {
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib:執行方法前。。。");
//調用原有方法
Object invoke = methodProxy.invokeSuper(object, args);
// Object invoke = method.invoke(t,args); 作用等同與上面。
System.out.println("Cglib:執行方法後。。。");
return invoke;
}
});
//生成代理對象
Object proxyObj = en.create();
return proxyObj;
}
}
在main類中測試
/**
* @author 陽光大男孩!!!
*/
public class Main {
public static void main(String[] args) {
Seeker seeker = new Seeker();
Action action = (Action) CglibFactory.getProxy(seeker);
action.givePresent();
}
}
執行結果
Cglib:執行方法前。。。
追求者送禮物
Cglib:執行方法後。。。
總結
代理模式能夠讓我們在不修改類代碼的情況下,對方法進行增強。
這也是spring aop 的一部分思想,更像是一種約定編程。
代理模式分爲靜態代理和動態代理。
動態代理模式能夠動態創建代理對象,從而讓我們免去創建代理類的麻煩。
參考
https://blog.csdn.net/qq_34178598/article/details/78630934#comments