设计模式学习——代理模式(2)

上一篇我们简单介绍了什么是代理模式,但我们说的仅仅是静态代理,所谓静态代理即代理类和目标类在代码中是确定的,因此称为静态,这种方式存在以下问题:

由于在使用时需要提前定义好代理类并实现对应主题的接口方法,随着需要代理的主题增加,代理类也会增加,导致项目中出现大量类情况,不易于项目维护。

那么有没有什么方式可以仅仅在我们需要的时候创建代理呢?答案是:动态代理

一、什么是动态代理

动态代理就是在实现阶段不需要关心代理谁,而在运行阶段才指定代理哪一个对象。那具体该如果使用呢?我们还是以代购为例,使用了动态代理后整个代购的类图如下:

从类图中我们看到增加了一个InvocationHandler和PaymentIH,他俩的作用就是产生一个对象的代理对象,其中Invocation是JDK提供的动态代理接口,对被代理类的方法进行代理,接下来我们看下具体代码的实现:

(1)PaymentIH

public class PaymentIH implements InvocationHandler {
​
    private Object obj;
​
    public PaymentIH(Object obj) {
        this.obj = obj;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前往海外某一大型商场");
        Object result = method.invoke(obj, args);
        System.out.println("携带商品回国");
        return result;
    }
}

其中invoke方法是InvocationHandler定义的必须实现的,它完成对真实方法的调用。

(2)客户端代码

public class Main {
    public static void main(String[] args)
    {
        IPaymentService paymentService = new WatchPaymentService();
        PaymentIH paymentIH = new PaymentIH(paymentService);
        IPaymentService proxy = (IPaymentService) Proxy.newProxyInstance(
                paymentService.getClass().getClassLoader(),
                new Class[] {IPaymentService.class}, paymentIH);
        proxy.pay();
    }
}

我们通过Porxy.newProxyInstance生成代理对象,此方法参数说明如下:

  • ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的

  • Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型

  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,把当前执行目标对象的方法作为参数传入

其实在这里我们也就会发现,使用JDK的动态代理有一个限制,那就是被代理类需要实现接口,否则无法代理。

我们来验证一下,我们将客户端代码修改如下后执行:

// WatchPaymentService去掉接口
public class WatchPaymentService {
​
    public void pay() {
        System.out.println("购买一块浪琴手表");
    }
}
​
public class Main {
    public static void main(String[] args)
    {
        WatchPaymentService paymentService = new WatchPaymentService();
        PaymentIH paymentIH = new PaymentIH(paymentService);
        IPaymentService proxy = (IPaymentService) Proxy.newProxyInstance(
                paymentService.getClass().getClassLoader(),
                new Class[] {WatchPaymentService.class}, paymentIH);
        proxy.pay();
    }
}

我们执行后发现如下报错:

 

二、动态代理与静态代理的区别

  • 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。

  • 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。

  • 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

posted @ 2021-08-01 18:59  阿拉懒神灯  阅读(45)  评论(0编辑  收藏  举报