设计模式之代理模式

定义

为其他对象提供一种代理以控制对这个对象的访问。
复制代码

优点

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

缺点

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

结构

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。(共同接口)
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。(真实对象的类)
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。(代理对象)

image.png

演示

最典型的案例就是代购,如需要购买外国的衣服,就需要在电商平台进行购买;这样外国商户就是真实对象,电商平台就是代理对象; 1、定义卖衣服接口

public interface SellClothes {
    void sellClothes();
}
复制代码

2、定义真实对象-商户

public class Merchant implements SellClothes{
    @Override
    public void sellClothes() {
        System.out.println("购买衣服成功!");
    }
}
复制代码

3、定义代理对象-平台

public class Platform implements SellClothes{
    private SellClothes sellClothes;
    public Platform(SellClothes sellClothes) {
        this.sellClothes = sellClothes;
    }
    @Override
    public void sellClothes() {
        sellClothes.sellClothes();
    }
}
复制代码

4、客户购买

public class Client {
    public static void main(String[] args) {
        Merchant merchant = new Merchant();
        Platform proxy = new Platform(merchant);
        proxy.sellClothes(); // 购买衣服成功!
    }
}
复制代码

总结

这个例子就是一个静态代理,简单明了,但是缺陷也明显:无法适配所有代理场景,如果有新的需求,需要修改代理类,不符合软件工程的开闭原则

动态代理:AOP

因为静态代理的缺陷,动态代理在静态代理基础上做了改动,提高了可维护性和可扩展性;要代理的类不需要提前建好代理类,而是通过运行时反射机制创建。

AOP动态代理主要有两种实现方式,JDK动态代理和CGLIB动态代理

JDK动态代理

JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口,JDK动态代理的核心是InvocationHandler接口和Proxy类。

在静态代理演示代码上做修改

1、代理类修改

public class Platform implements InvocationHandler {
    private Object object;
    public Platform(Object object) {
        this.object = object;
    }
    /**
    * Object proxy:代理对象
    * Method method:真正执行的方法
    * Object[] agrs:调用第二个参数 method 时传入的参数列表值
    */
    @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;
    }
}
复制代码

2、客户端调用

public class Client {
    public static void main(String[] args) {
        Merchant merchant = new Merchant();
        Platform proxy = new Platform(merchant);
        // Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
        // ClassLoader loader:加载动态代理类的类加载器
        // Class<?>[] interfaces:目标对象实现接口类型,使用泛型方式类型
        // InvocationHandler h:当执行目标对象的方法时,会先调用InvocationHandler的invoke方法, 同时把当前执行目标对象的方法作为参数传入
        SellClothes sellClothes = (SellClothes) Proxy.newProxyInstance(merchant.getClass().getClassLoader(), 
            merchant.getClass().getInterfaces(), proxy);
        sellClothes.sellClothes();
        // 调用前。。。
        // 购买衣服成功!
        // 调用后。。。
    }
}
复制代码

CGLIB动态代理

Cglib(Code generation Library)不是 JDK 自带的动态代理,它需要导入第三方依赖,它是一个字节码生成类库,能够在运行时动态生成代理类对 Java类 和 Java接口 扩展。

Cglib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。

所以如果需要代理的类没有实现接口,可以选择 Cglib 作为实现动态代理的工具。

两个核心的类MethodInterceptor接口 和 Enhancer类,前者是实现一个代理工厂的根接口,后者是创建动态代理对象的类

在静态代理演示代码里修改

1、真实对象-商户,不再需要实现接口

public class Merchant{
    public void sellClothes() {
        System.out.println("购买衣服成功!");
    }
}
复制代码

2、代理类-平台

public class Platform implements MethodInterceptor {
    /**
    * Object o:被代理对象
    * Method method:被拦截的方法
    * Object[] objects:被拦截方法的所有入参值
    * MethodProxy methodProxy:方法代理,用于调用原始的方法
    */
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用前。。。");
        methodProxy.invokeSuper(object, args);
        System.out.println("调用后。。。");
        return null;
    }
}
复制代码

3、调用类

public class Client {
    public static void main(String[] args) {
        Platform proxy = new Platform();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Merchant.class);
        //回调方法的参数为代理类对象Platform,最后增强目标类调用的是代理类对象Platform中的intercept方法
        enhancer.setCallback(proxy);
        Merchant merchant = (Merchant) enhancer.create();
        merchant.sellClothes();
        // 调用前。。。
        // 购买衣服成功!
        // 调用后。。。
    }
}
复制代码

总结

CGLIB 可以代理大部分类;而 JDK Proxy 仅能够代理实现了接口的类

CGLIB 采用动态创建被代理类的子类实现方法拦截,子类内部重写被拦截的方法,所以 CGLIB 不能代理被 final 关键字修饰的类和方法

总的来说:若目标对象有实现接口,我们可以用JDK代理; 若目标对象没有实现接口,则用Cglib代理;当然,实现了接口也可以用Cglib代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值