设计模式笔记

设计模式笔记(十三): 代理模式

2018-08-01  本文已影响13人  yeonon

代理模式是比较重要的一种设计模式,应用场合也比较多,例如远程调用(RMI, RPC等),还有Spring的AOP的实现原理。

代理模式有静态代理和动态代理两种,动态代理常见的又有两种实现方式,一种是基于JDK的动态代理,另一种是基于字节码技术(例如Cglib)的动态代理。

代理模式的基本两个组件是:

  1. 目标类
  2. 代理类
  3. 抽象接口,上述两个类都要实现这个接口

静态代理

所谓静态代理,即代理类的又程序员编写的,是在应用运行之前就已经存在的。如下代码所示:

//主题接口,代理类和目标类都会实现这个接口
public interface Subject {
    void hello();
}

//目标类,代理类的目标
public class RealObject implements Subject{


    @Override
    public void hello() {
        System.out.println("real object sad hello!");
    }
}

//代理类,持有目标类的引用
public class RealObjectProxy implements Subject {

    private Subject realObject;

    public RealObjectProxy(Subject realObject) {
        this.realObject = realObject;
    }

    @Override
    public void hello() {
        System.out.println("before real object");
        realObject.hello();
        System.out.println("after real object");
    }
}

//工厂类,并不是代理模式里必须的,这里只是为了客户端调用更加简洁
public class SubjectProxyFactory {

    private SubjectProxyFactory() {}

    public static Subject getRealObjectProxy() {
        return new RealObjectProxy(new RealObject());
    }
}

//测试类
public class Main {

    public static void main(String[] args) {
        Subject proxy = SubjectProxyFactory.getRealObjectProxy();
        proxy.hello();

    }
}

运行代码,应该能在控制台看到如下结果:

before real object
real object sad hello!
after real object

这就是代理模式的效果,有了代理模式,我们可以在目标类的方法调用之前和调用之后做一些处理,而不需要修改目标类的代码。例如记录日志等。

但是静态代理有一个很严重的问题,那就是代理类需要人工编写,如果应用中需要代理的类很多,那是不是要写很多个代理类,而且有些代理类的前置处理和后置处理还可能一样,这无疑增加了代码维护难度。所以我们急切需要能自动生成代理类的技术,这种技术就是动态代理

动态代理

动态代理常见的有两种实现方式:

  1. 基于JDK的动态代理
  2. 基于Cglib等字节码技术的动态代理。

基于JDK的动态代理

代码如下:

public interface Subject {

    void hello();
}

public class RealObject implements Subject {
    @Override
    public void hello() {
        System.out.println("real object say hello!");
    }
}

//代理处理类,这个类会动态的生成代理类
public class ProxyHandler implements InvocationHandler {

    private Object target;


    public Object getProxy(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                        target.getClass().getInterfaces(),
                                        this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("before object");
        result = method.invoke(target, args);
        System.out.println("after object");
        return result;
    }
}

public class Main {

    public static void main(String[] args) {
        ProxyHandler proxyHandler = new ProxyHandler();
        Subject subject = (Subject) proxyHandler.getProxy(new RealObject());
        subject.hello();
    }
}

运行代码,可以发现结构和上面的静态代理并无太大差别,但是更加的通用了,现在我们可以用这个代理处理类来自动生成多个代理类而不需要自己手动编写了,方便了许多,代码也容易维护了许多。

不过基于JDK的动态代理又一个限制,即目标类必须要实现某一个接口,否则会出现强转失败,如果不进行强转就无法调用方法(除了Object类的方法),那么这个代理就没有什么意义。

这一点可以从下面这行代码看出来:

return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                        target.getClass().getInterfaces(),
                                        this);

这里三个参数,第一个是classLoader,第二个就是目标类接口,第三个是InvocationHandler。如果目标类没有实现接口,那么就无法通过反射获取类信息,最终强转也就无法成功。

基于Cglib的动态代理

需要加入Cglib的相关依赖。

代码如下:

public interface Subject {

    void hello();
}


public class RealObject {

    public void hello() {
        System.out.println("real object say hello!");
    }
}

//代理处理类
public class CglibProxy implements MethodInterceptor {

    public Object getProxy(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        enhancer.setClassLoader(target.getClass().getClassLoader());
        return enhancer.create();
    }
    
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        System.out.println("before handler");
        result = methodProxy.invokeSuper(o, objects);
        System.out.println("after handler");
        return result;
    }
}

public class Main {

    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        RealObject object = (RealObject) cglibProxy.getProxy(new RealObject());
        object.hello();
    }
}

和基于JDK的动态代理不同的是,这里的代理处理类实现的是MethodInterceptor,是Cglib包里的一个类。可以看到,在获取代理的方法里,我们并没有获取目标类的接口信息,也就是说我们的目标类并不需要实现任何接口(当然,实现也不会有错)。这使得这种方式的动态代理更加灵活。

那为什么不需要实现接口呢?因为Cglib在生成代理类的时候,代理类其实是目标类的子类,这个子类覆盖目标类的所有方法,所以基于Cglib的这种方式,目标类不能有final方法(final方法不能被重写覆盖),其本身也不能是final类(final类不能被继承)。

代理模式的特点

  1. 解耦。通过代理类去操作目标类,并不直接操作目标类,这使得目标类和客户端之间解耦,目标类的改变不会影响到客户端,最多影响到代理类。
  2. 可以实现AOP。AOP可以向已有代码动态的织入逻辑,例如日志记录,利用这种方式的AOP可以在不改变现有类的情况下添加功能。
  3. 不足是大量的使用动态代理会造成应用内存中存在太多的对象。这可能会很麻烦。

小结

现在可以给出代理模式的定义了:代理模式为另一个对象提供一个替身或占位符以控制整个对象的访问。

上一篇 下一篇

猜你喜欢

热点阅读