Java核心Design Pattern

Proxy - 代理模式

2018-08-15  本文已影响31人  DjangoW

注意:全文是基于Java来描述和实现的!
代理模式的实现有很多种方法:静态代理,动态代理(又分为反射实现的动态代理,CGLib通过修改字节码文件实现的动态代理)。代理模式的目的是解耦Client和Subject,Proxy作为Client和Subject之间的中间人接受Client的操作请求并和Subject交互。通过这种Proxy的方式可以在原操作的基础上添加额外的功能和操作,比如对数据库的读写操作结束之后对读写结果做缓存处理,比如对指定的操作之前和之后做日志的记录。

静态代理

代理模式(静态)的类图如下:

Proxy Pattern

Java实现如下:
Subject接口

interface Subject{
    void request();
}

RealSubject类

class RealSubject implements Subject{
    public void request(){
        System.out.println("RealSubject request invoke!");
    }
}

Proxy类

class Proxy implements  Subject{
    private Subject subject;
    public Proxy(Subject subject){
        this.subject = subject;
    }
    private void preRequest(){
        System.out.println("preRequest");
    }
    private void postRequest(){
        System.out.println("postRequest");
    }
    public void request(){
        preRequest();
        subject.request();
        postRequest();
    }
}

Client类

class Client{
    public static void main(String [] args){
        Proxy subjectProxy = new Proxy(new RealSubject());
        subjectProxy.request();
    }
}

Proxy的作用就是充当Client和Subject的中间人,告诉Proxy要找Subject做什么,然后Proxy会负责整个调用流程,包括调用前调用后甚至出现Exception的时候要做的额外的工作。

举个更容易理解的例子:代购。Client是买家,Proxy代购者,Subject是卖家。Client只需要告诉Proxy想买什么产品,而不用管这个产品在怎么买(是online还是线下)和去哪儿买(是本国还是要出国),整个中间所有的细节都由Proxy来负责,而在Subject那边买东西只是整个购买流程的一部分。

如上就是一个简单的代理模式的实现,也是静态代理的实现。静态代理模式下,如果要针对其他操作提供代理,就需要针对不同的Subject接口实现不同的Proxy代理类。如果代理很多,会造成高代码重复率。
举一个实际应用的例子,你想通过代理为数据库读写操作加上缓存机制,在读写操作结束后将结果缓存到内存中,数据库读写操作如果都要如上述实现静态代理就太啰嗦了。解决这个问题的一个方法是使用通过Java的反射机制实现的动态代理来代替静态代理。

动态代理

interface Subject{
    void request();
}

RealSubject类

class RealSubject implements Subject{
    public void request(){
        System.out.println("RealSubject request invoke!");
    }
}

动态代理类实现:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy implements InvocationHandler {
    
    Object tar;
    
    public Object bind(Object tar) {
        this.tar = tar;
        return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // operations before call the method
        System.out.println("print before dynamic proxy invoke method of target");
        return method.invoke(tar, args);
               // operations after call the method
        System.out.println("print before dynamic proxy invoke method of target");
    }
}

调用代码:

class Client{
    public static void main(String [] args){
        Subject vender = (Subject) new DynamicProxy().bind(new RealSubject());
        vender.request();
    }
}

针对不同的接口Subject,不需要再提供不同的Proxy代理类的实现,只需要在通过new DynamicProxy()创建代理的时候传入实现了该接口的instance就好。

可以看出,Java通过反射机制实现动态代理需要被代理对象实现统一的接口,如果想代理没有实现特定接口的对象怎么办呢?这时可以考虑使用CGLib实现的动态代理模式

上一篇下一篇

猜你喜欢

热点阅读