代理模式

一、模式名

代理, Proxy

二、解决的问题

“代理”这个词我们应该不陌生,在我们的生活中经常使用代理。例如:很多去国外旅游的人都会通过旅游中介公司购买机票、国外景点门票以及规划路线和寻找导游等,其实这就是一种代理模式,把自己不想直接做和不了解的事情交给专业的人去做,这样更加放心,效率也更高。

在软件设计中,同样如此,很多优秀的开发框架都是会使用代理模式。在软件设计中的代理模式主要用于:

1. 安全问题:通过使用代理,可以避免客户端直接访问真实的处理对象,可以控制对象的访问权限;

2. 功能扩展:在很多框架中使用代理来扩展某些功能。比如在Spring中的AOP就利用了代理模式,通过在代理类中织入日志、安全等功能,实现对委托类对象功能的扩展。

三、解决方案

代理模式的UML图如下图所示:

clipboard

其中Subject为接口,定义需要的方法,代理类ProxySubject和代理类RealSubject都实现Subject接口,同时ProxySubject依赖RealSubject对象完成具体的操作。

具体的代理主要分为两类:静态代理和动态代理。静态代理和动态代理的区别在于静态代理的代理类由程序员编写,代理对象是在通过编译该代理类生成,而动态代理的代理对象是在程序运行时通过反射机制生成。

1. 静态代理

静态代理就是手动编写代理类,代理类通过引用委托类对象完成具体操作。Java代码如下。

interface Subject {
    void method1();
}

RealSubject implements Subject {
    void method1() {
        // 实现方法体
        ...
    }
}

ProxySubject implements Subject {
    private Subject subject;
    
    public ProxySubject(Subject subject) {
        this.subject = subject;
    }
    
    void method1() {
        // 执行前
        before();
        // 调用委托类对象完成具体操作
        subject.method1();
        // 执行后
        after();
    }
    
    void before() {}
    void after() {}
}

public class Main {
    public static void main(String[] args) {
        Subject subject = new ProxySubject(new RealSubject());
        subject.method1();
    }
}

可以看到静态代理需要针对每个委托类编写一个代理类,如果接口修改,代理类也需要做相应修改。

2. 动态代理

动态代理不需要程序员手动编写代理类,动态代理分为JDK代理和CGLib代理,前者针对其父类是接口的情况,后者针对父类是普通类的情况。

JDK 代理 Java代码如下所示。

public class DynamicProxyHandler implements InvocationHandler {
    private Object target;
    
    public DynamicProxyHandler() {
        this.obj = target;    
    }
    
    public Object invoke(Object obj, Method method, Object[] args) 
                            throws Throwable {
        // before
        before();
        Object result = method.invoke(target, args);
        // after
        after();
        return result;
    }
    
    void before() {}
    void after() {}
}

public class Main {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                Main.class.getClassLoader(), new Class[] {Subject.class}, 
                new DynamicProxyHandler(subject));
        proxySubject.method1();
    }
}

其中,Proxy.newProxyInstance()方法有三个参数,桉顺序分别是类加载器,委托类实现的接口,代理类动态处理器。

JDK代理的缺点就是只能针对interface代理,下面CGLib代理使用字节码技术实现针对class的代理。

CGLib 代理 Java代码如下所示。

 

class Subject {
    void method1() {}
}

RealSubject extends Subject {
    void method1() {
        // 方法体
        ...
    }
}

public class CglibProxy implements MethodInterceptor {
    private Object target;
    
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperClass(this.target.getClass());
        enhancer.setCallBack(this);
        return enhancer.create();
    }
    
    public Object intercept(Object object, Method method, Object[] args, 
                    MethodProxy methodProxy) throws Throwable {
        // before
        before();
        Object object = methodProxy.invoke(target, args);
        // after
        after();
        
        return object;
    }
    
    void before();
    void after();
}

public class Main {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        CglibProxy proxy = new CglibProxy();
        Subject proxySubject = (Subject) 
                                    proxy.getInstance(subject);
        proxySubject.method1();
    }
}

常见应用场景:

1. Spring中的 AOP

posted @ 2019-06-20 23:59  锢浪熟阳  阅读(186)  评论(0编辑  收藏  举报