多年以前在學(xué)習(xí)設(shè)計(jì)模式時,一直以為代理就是這個事情我不做了,交給別人做。現(xiàn)在重學(xué)設(shè)計(jì)模式,才發(fā)現(xiàn)自己還是太天真,而且經(jīng)歷過這么多事情也明白,就算再好的代理,還是需要自己做一些事情的。
代理模式:在不修改一個類的前提下,實(shí)現(xiàn)一個該類的代理類來,代理類需要實(shí)現(xiàn)所有行為,并且可以根據(jù)需要在行為中增加其他邏輯和細(xì)節(jié)。
舉個例子,王晶需要找周星馳拍一部新電影,即希望調(diào)用Zhou.act();
。但是王晶不能直接聯(lián)系周星馳,他需要聯(lián)系星爺?shù)慕?jīng)紀(jì)人,或者我們稱之為代理人,去商討拍電影的細(xì)節(jié)工作。這個時候,這位代理人其實(shí)需要做更多細(xì)節(jié)性的工作,比如說
ZhouProxyer.bargainContract();
ZhouProxyer.schedule();
ZhouProxyer.act(); // 實(shí)際調(diào)用Zhou.act();
ZhouProxyer.complete();
ZhouProxyer.summary();
我們可以看到,作為星爺?shù)拇砣耍鋵?shí)他在星爺真正拍電影前后做了很多事情,這些事不需要星爺操心,只需保證自己的act()正常即可。
從這個例子中,我們可以進(jìn)行抽象,將主要的主體提煉出來:
- Client:客戶(王晶),類的使用者;
- Subject:定義行為的抽象類或接口;
- RealSubject:真實(shí)的底層實(shí)現(xiàn)類,實(shí)現(xiàn)了Subject定義的行為(周星馳);
- Proxy:代理類,也實(shí)現(xiàn)了Subject定義的行為,且擁有RealSubject的一個實(shí)例(經(jīng)紀(jì)人);
現(xiàn)在,我們用三種代理模式來實(shí)現(xiàn)上述的這個關(guān)系。
靜態(tài)代理
靜態(tài),說白了就是寫死的代碼,按照你寫的代碼實(shí)現(xiàn)代理機(jī)制。上面說的幾個主要主體,都需要自己定義和生成。
現(xiàn)在我們先來將Subject接口定義出來。
package com.designpattern.proxy;
public interface IActor {
public void act();
}
而后是星爺?shù)念悾鼘?shí)現(xiàn)了IActor接口,其中為了方便,隨便用了一個勤漢單例:
package com.designpattern.proxy;
public class Zhou implements IActor {
private static Zhou instance = new Zhou();
public static Zhou getInstance(){
return instance;
}
@Override
public void act() {
System.out.println("Here comes Zhou's newest movie: Crazy biscuit!");
}
}
然后我們再定義一個Zhou的代理人,同樣也實(shí)現(xiàn)了IActor:
package com.designpattern.proxy;
public class ZhouProxy implements IActor {
private IActor zhou;
public ZhouProxy(IActor zhou){
this.zhou = zhou;
}
@Override
public void act() {
bargainContract();
schedule();
zhou.act();
complete();
summary();
}
private void complete() {
System.out.println("Movie complete.");
}
private void summary() {
System.out.println("Movie's summary.");
}
private void schedule() {
System.out.println("Make a schedule for the new movie.");
}
private void bargainContract() {
System.out.println("Bargain the price of movie contract");
}
}
最后我們寫一個王晶出來,王晶的main方法就是拍電影:
package com.designpattern.proxy;
public class Wang {
public static void main(String[] args){
IActor zhouProxy = new ZhouProxy(Zhou.getInstance());
zhouProxy.act();
}
}
好的,我們運(yùn)行一下,看到輸出的結(jié)果:
Bargain the price of movie contract
Make a schedule for the new movie.
Here comes Zhou's newest movie: Crazy biscuit!
Movie complete.
Movie's summary.
從代碼中我們能看到,王晶其實(shí)只找到了代理人,但是經(jīng)過調(diào)用代理人的act方法,我們?nèi)阅芡瓿勺屩苄邱Y拍電影的需求。這樣下來,其實(shí)靜態(tài)代理模式已經(jīng)實(shí)現(xiàn)了。
動態(tài)代理
動態(tài),就是在運(yùn)行時能夠根據(jù)實(shí)際情況進(jìn)行判斷并生成代理類的模式。它不需要具體定義某個類,只需要編寫一些代理類需要做的事情即可。我們直接運(yùn)用JDK中InvocationHandler來定義代理人,利用Proxy類來獲得動態(tài)代理的實(shí)例。
我們先來看接口InvocationHandler,其實(shí)就定義了一個方法:
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
那么我們現(xiàn)在寫一個使用動態(tài)代理的代理類ActorProxy,它實(shí)現(xiàn)了InvocationHandler:
package com.designpattern.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ActorProxy implements InvocationHandler {
private Object actor ;
public ActorProxy(Object actor){
super();
this.actor = actor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
bargainContract();
schedule();
method.invoke(actor, args);
complete();
summary();
return null;//如果method有返回,也可以返回其值
}
private void complete() {..}
private void summary() {..}
private void schedule() {..}
private void bargainContract() {..}
}
我們再重寫Wang的main函數(shù),調(diào)用這個類進(jìn)行動態(tài)代理操作:
public class Wang {
public static void main(String[] args){
IActor actor = (IActor) Proxy.newProxyInstance(Zhou.getInstance().getClass().getClassLoader(), Zhou.getInstance().getClass().getInterfaces(), new ActorProxy(Zhou.getInstance()));
actor.act();
}
}
而結(jié)果和靜態(tài)代理的一致。其中Proxy.newProxyInstance()方法,不用往里看,我們先看一下它的參數(shù):
- ClassLoader loader:表示需要代理的類的加載器;
- Class<?>[] interfaces:表示需要代理的類實(shí)現(xiàn)的接口;
- InvocationHandler h:即代理類;
根據(jù)參數(shù)其實(shí)我們能夠想到,代理類其實(shí)就是在相同的加載器loader中,加載一個實(shí)現(xiàn)了相同interfaces接口的代理類,并且該代理類的行為是在第三個參數(shù)h中定義的。
Cglib
不知道大家有沒有想到一個問題,如果我想實(shí)現(xiàn)一個沒有實(shí)現(xiàn)任何接口的類的代理類,或者說一個類雖然實(shí)現(xiàn)了某些接口,但是我希望針對類中非重載的方法進(jìn)行代理,應(yīng)該如何實(shí)現(xiàn)呢?其實(shí)這也是在Spring框架中需要解決的一個問題,而答案自然也在Spring框架中找。
使用Cglib庫,需要注意以下幾點(diǎn):
- 需要引入cglib的jar文件,Spring的核心包(spring-core-x.x.x.jar)中已經(jīng)包括了Cglib功能,當(dāng)然你也可以只下Cglib的包;
- 代理的類不能為final,否則報錯;
- 目標(biāo)對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法;
這里想說一句,網(wǎng)上各種教程,全部都是在說OSCHINA的Gradle源,但是人家早就在2015年06月29日停止服務(wù)了。所以我在網(wǎng)上找到了阿里爸爸的Gradle庫地址
repositories {
def REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public/'
all { ArtifactRepository repo ->
if(repo instanceof MavenArtifactRepository){
def url = repo.url.toString()
if (url.startsWith('https://repo1.maven.org/maven2') || url.startsWith('https://jcenter.bintray.com/')) {
project.logger.lifecycle "Repository ${repo.url} replaced by $REPOSITORY_URL."
remove repo
}
}
}
maven {
url REPOSITORY_URL
}
}
然后如果沒有配置過Spring的話,可以直接下Cglib的包:
compile group: 'cglib', name: 'cglib', version: '3.2.2'
OK,準(zhǔn)備工作已經(jīng)完畢了,現(xiàn)在我們來定義一個沒有實(shí)現(xiàn)接口的Zhou:
package com.designpattern.proxy;
public class OnlyZhou {
public void onlyAct(){
System.out.println("Only Zhou Action.");
}
public final void finalAct() {
System.out.println("This is a final act!");
}
}
而后,我們定義一個使用Cglib庫的代理:
package com.designpattern.proxy;
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 ActorCglibProxy implements MethodInterceptor {
private ActorCglibProxy(){}
public static OnlyZhou getProxyInstance(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OnlyZhou.class);
enhancer.setCallback(new ActorCglibProxy());
return (OnlyZhou) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
bargainContract();
schedule();
proxy.invokeSuper(obj, args); //注意,此處不同!!!
complete();
summary();
return null;
}
private void complete() {..}
private void summary() {..}
private void schedule() {..}
private void bargainContract() {..}
}
然后我們來調(diào)用這個新的代理,先調(diào)用了一個正常的方法,而后調(diào)用了一個final的方法:
public class Wang {
public static void main(String[] args){
OnlyZhou onlyZhou = ActorCglibProxy.getProxyInstance();
onlyZhou.onlyAct();
onlyZhou.finalAct();
}
}
可以看到結(jié)果為:
Bargain the price of movie contract
Make a schedule for the new movie.
Only Zhou Action.
Movie complete.
Movie's summary.
This is a final act!
其中前五行是被截獲(intercept)的方法輸出的,而最后一行是出自一個final方法,上面說了final方法不會被截獲,所以只輸出了一行信息。