Java設(shè)計(jì)模式之-代理模式(Proxy)

多年以前在學(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ù):

  1. ClassLoader loader:表示需要代理的類的加載器;
  2. Class<?>[] interfaces:表示需要代理的類實(shí)現(xiàn)的接口;
  3. 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):

  1. 需要引入cglib的jar文件,Spring的核心包(spring-core-x.x.x.jar)中已經(jīng)包括了Cglib功能,當(dāng)然你也可以只下Cglib的包;
  2. 代理的類不能為final,否則報錯;
  3. 目標(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方法不會被截獲,所以只輸出了一行信息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容