Java设计模式之-代理模式(Proxy)

多年以前在学习设计模式时,一直以为代理就是这个事情我不做了,交给别人做。现在重学设计模式,才发现自己还是太天真,而且经历过这么多事情也明白,就算再好的代理,还是需要自己做一些事情的。

代理模式:在不修改一个类的前提下,实现一个该类的代理类来,代理类需要实现所有行为,并且可以根据需要在行为中增加其他逻辑和细节。

举个例子,王晶需要找周星驰拍一部新电影,即希望调用Zhou.act();。但是王晶不能直接联系周星驰,他需要联系星爷的经纪人,或者我们称之为代理人,去商讨拍电影的细节工作。这个时候,这位代理人其实需要做更多细节性的工作,比如说

  • ZhouProxyer.bargainContract();
  • ZhouProxyer.schedule();
  • ZhouProxyer.act(); // 实际调用Zhou.act();
  • ZhouProxyer.complete();
  • ZhouProxyer.summary();

我们可以看到,作为星爷的代理人,其实他在星爷真正拍电影前后做了很多事情,这些事不需要星爷操心,只需保证自己的act()正常即可。
从这个例子中,我们可以进行抽象,将主要的主体提炼出来:


Java设计模式之-代理模式(Proxy)_第1张图片
代理模式
  • Client:客户(王晶),类的使用者;
  • Subject:定义行为的抽象类或接口;
  • RealSubject:真实的底层实现类,实现了Subject定义的行为(周星驰);
  • Proxy:代理类,也实现了Subject定义的行为,且拥有RealSubject的一个实例(经纪人);

现在,我们用三种代理模式来实现上述的这个关系。

静态代理

静态,说白了就是写死的代码,按照你写的代码实现代理机制。上面说的几个主要主体,都需要自己定义和生成。
现在我们先来将Subject接口定义出来。

package com.designpattern.proxy;

public interface IActor {
    public void act();

}

而后是星爷的类,它实现了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的代理人,同样也实现了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();
    }
}

好的,我们运行一下,看到输出的结果:

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.

从代码中我们能看到,王晶其实只找到了代理人,但是经过调用代理人的act方法,我们仍能完成让周星驰拍电影的需求。这样下来,其实静态代理模式已经实现了。


动态代理

动态,就是在运行时能够根据实际情况进行判断并生成代理类的模式。它不需要具体定义某个类,只需要编写一些代理类需要做的事情即可。我们直接运用JDK中InvocationHandler来定义代理人,利用Proxy类来获得动态代理的实例。

我们先来看接口InvocationHandler,其实就定义了一个方法:

package java.lang.reflect;

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

那么我们现在写一个使用动态代理的代理类ActorProxy,它实现了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函数,调用这个类进行动态代理操作:

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();
    }
}

而结果和静态代理的一致。其中Proxy.newProxyInstance()方法,不用往里看,我们先看一下它的参数:

  1. ClassLoader loader:表示需要代理的类的加载器;
  2. Class[] interfaces:表示需要代理的类实现的接口;
  3. InvocationHandler h:即代理类;

根据参数其实我们能够想到,代理类其实就是在相同的加载器loader中,加载一个实现了相同interfaces接口的代理类,并且该代理类的行为是在第三个参数h中定义的。

Cglib

不知道大家有没有想到一个问题,如果我想实现一个没有实现任何接口的类的代理类,或者说一个类虽然实现了某些接口,但是我希望针对类中非重载的方法进行代理,应该如何实现呢?其实这也是在Spring框架中需要解决的一个问题,而答案自然也在Spring框架中找。

使用Cglib库,需要注意以下几点:

  1. 需要引入cglib的jar文件,Spring的核心包(spring-core-x.x.x.jar)中已经包括了Cglib功能,当然你也可以只下Cglib的包;
  2. 代理的类不能为final,否则报错;
  3. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;

这里想说一句,网上各种教程,全部都是在说OSCHINA的Gradle源,但是人家早就在2015年06月29日停止服务了。所以我在网上找到了阿里爸爸的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,准备工作已经完毕了,现在我们来定义一个没有实现接口的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() {..}
}

然后我们来调用这个新的代理,先调用了一个正常的方法,而后调用了一个final的方法:

public class Wang {
    public static void main(String[] args){
        OnlyZhou onlyZhou = ActorCglibProxy.getProxyInstance();
        onlyZhou.onlyAct();
        onlyZhou.finalAct();
    }
}

可以看到结果为:

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方法不会被截获,所以只输出了一行信息。

你可能感兴趣的:(Java设计模式之-代理模式(Proxy))