23种设计模式之8.代理模式

130 阅读4分钟

代理模式就是提供一个代理对象,代理对象持有被代理对象的引用,通过代理对象对被代理对象进行增强。比如你想要买房子,你只想买个房子,并不想关注买房子附带的其他的事,那么就可以找中介,中介给你找房子,带你签合同。这个例子中你就是被代理对象,你的行为只有一个那就是交易,中介呢是代理对象,他要在你买房子前给你找房子,做清洁啊什么的,买房子后带你签合同,这些附加的行为都是代理对象对你行为的增强。

代理模式一共有两类:

  1. 静态代理
  2. 动态代理

在java中常见的动态代理又有两种分别是基于JDK的动态代理和基于CGlib的动态代理,下面就用代码理解一下这几种代理模式。

现在有一个交易的接口与一个房屋交易的实现类:

public interface TransactionAble {
    void transaction();
}

public class TransactionHouse implements TransactionAble {
    @Override
    public void transaction() {
        System.out.println("交易房屋");
    }
}

现在就可以通过代理类对房屋交易进行增强。

静态代理

静态代理就是手动编写代理类对被代理类进行增强:

public class StaticProxyTransaction implements TransactionAble{

    private TransactionAble transactionAble;

    public StaticProxyTransaction(TransactionAble transactionAble) {
        this.transactionAble = transactionAble;
    }
    @Override
    public void transaction() {
        System.out.println("交易前清洁");
        transactionAble.transaction();
        System.out.println("交易后签合同");
    }
}

使用时直接使用代理对象进行操作即可:

public class Main {
    public static void main(String[] args) {
        StaticProxyTransaction staticProxyTransaction = new StaticProxyTransaction(new TransactionHouse());
        staticProxyTransaction.transaction();
    }
}

运行结果如下图所示:

image.png

基于JDK的动态代理

基于JDK的动态代理是通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类,我们只需编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建:

public class JdkProxyTransaction implements InvocationHandler {

    private Object object;

    public JdkProxyTransaction(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("交易前清洁");
        Object invoke = method.invoke(object, args);
        System.out.println("交易后签合同");
        return invoke;
    }
}

使用时使用Proxy.newProxyInstance即可动态的创建出代理对象:

public class Main {

    public static void main(String[] args) {
        TransactionAble transactionAble = new TransactionHouse();
        TransactionAble proxyBuyHouse = (TransactionAble) Proxy.newProxyInstance(
            TransactionAble.class.getClassLoader(), 
            new Class[]{TransactionAble.class}, 
            new JdkProxyTransaction(transactionAble)
        );
        proxyBuyHouse.transaction();
    }
}

运行结果如下图所示:

image.png

基于CGlib的动态代理

可以看到Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)的第二个参数为接口的数组,那就是说必须是实现了接口的类才能使用Jdk动态代理,那没有实现接口的类该怎么动态代理呢?这里基于CGlib的动态代理就出场了,CGLib采用了底层的字节码技术,其原理是通过字节码技术为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势将横切逻辑织入目标对象。由于基于CGlib的动态代理采用的是继承,所以不能对final修饰的类进行代理。

使用CGlib动态代理首先要引入依赖:

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.8</version>
    </dependency>
</dependencies>

编写CGlib动态代理的拦截器:

public class CglibProxyTransaction implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("交易前清洁");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("交易后签合同");
        return invoke;
    }
}

创建代理对象,并执行目标方法:

public class Main {

    public static void main(String[] args) {
        //用来创建代理对象的增强器
        Enhancer enhancer = new Enhancer();
        //将被代理的类设置为父类
        enhancer.setSuperclass(TransactionHouse.class);
        //设置刚才定义的拦截器
        enhancer.setCallback(new CglibProxyTransaction());
        //创建代理对象
        TransactionHouse transactionHouse = (TransactionHouse) enhancer.create();
        //代理对象执行方法
        transactionHouse.transaction();
    }
}

运行结果如下图所示:

image.png

通过这三种方式都可以实现代理模式,对交易方法都进行了增强。

代理模式优点:

  1. 职责分明,各个类只干自己的事。
  2. 高扩展性,符合开闭原则的情况下对现有类进行扩展。
  3. 动态代理减少了类的数量,使用更加灵活。

代理模式缺点:

  1. 静态代理:一个接口需要一种代理类,类数量增多,接口修改对应代理类也得修改。
  2. 基于Jdk的动态代理:只能对实现接口的类进行代理。
  3. 基于CGlib的动态代理:不能对final类与final方法进行代理,经过测试对final类代理会报错,对final方法代理增强的部分会失效。