设计模式之代理模式(Proxy Pattern)【结构性模式】

在这里插入图片描述

为什么用结构性模式?

  • 结构性模式关注点“怎样组合对象/类?”所以我们关注下类的组合关系
  • 类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承)
  • 对象结构型模式关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象(组合)
  • 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式
  1. 适配器模式(Adapter Pattern):两个不兼容接口之间适配的桥梁。
  2. 桥接模式(Bridge Pattern):相同功能抽象化与实现化解耦,抽象与实现可以独立升级。
  3. 过滤器模式(Filter、Criteria Pattern):使用不同的标准来过滤一组对象。
  4. 组合模式(Composite Pattern):相似对象进行组合,形成树形结构。
  5. 装饰器模式(Decorator Pattern):向一个现有的对象添加新的功能,同时又不改变其结构。
  6. 外观模式(Facade Pattern):向现有的系统添加一个接口,客户端访问此接口来隐藏系统的复杂性。
  7. 享元模式(Flyweight Pattern):尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
  8. 代理模式(Proxy Pattren):一个类代表另一个类的功能。

🍭结构性模式之代理模式(Proxy Pattern)

🍎代理模式

  • 代理模式(Proxy Pattern),给某一个对象提供一个代理,并由代理对象控制原对象的引用,对象结构型模式。这种也是静态代理。

在这里插入图片描述

代理模式包含如下角色:

  • Subject:抽象主体角色(抽象类或接口)
  • Proxy:代理主体角色(代理对象类)
  • RealSubject:真实主体角色(被代理对象类)

代理模式分为:

  • 静态代理
  • JDK动态代理
  • Cglib动态代理
🍔静态代理代码实现
/**
 * 抽象主体。被代理角色能干什么
 */
public interface ManTikTok {
    void tikTok();
}

/**
 * Subject:主体
 */
public class ZhuicatTikTok implements ManTikTok {
    @Override
    public void tikTok() {
        System.out.println("张三 tiktok");
    }
}

/**
 * 代理一般都是和被代理对象属于同一个接口
 */
public class TikTokProxy implements ManTikTok {

    private ManTikTok manTikTok; // 被代理对象

    public TikTokProxy(ManTikTok manTikTok) {
        this.manTikTok = manTikTok;
    }

    @Override
    public void tikTok() {
        // 增强功能
        System.out.println("渲染直播间");
        manTikTok.tikTok();
    }
}
public class MainTest {
    public static void main(String[] args) {
        ManTikTok manTikTok = new ZhuicatTikTok();
        TikTokProxy tikTokProxy = new TikTokProxy(manTikTok);
        tikTokProxy.tikTok();
    }
}

渲染直播间
张三 tiktok

可以发现:静态代理就是装饰器,我们可以认为装饰器模式是代理模式的一种

使用静态代理时,每一次代理的代理对象不一样,就需要创建不同的代理类。

🍔JDK动态代理代码实现
/**
 * 抽象主体。被代理角色能干什么
 */
public interface ManTikTok {
    void tikTok();
}

public interface SellTikTok {
    Integer sell();
}

/**
 * Subject:主体
 */
public class ZhuicatTikTok implements ManTikTok,SellTikTok {
    @Override
    public void tikTok() {
        System.out.println("张三 tiktok");
    }

    @Override
    public Integer sell() {
        System.out.println("只要666");
        return 666;
    }

    public void haha() {
        System.out.println("哈哈");
    }
}

public class JdkTikTokProxy<T> implements InvocationHandler {

    private T target;

    public JdkTikTokProxy(T target) {
        this.target = target;
    }

    /**
     * ClassLoader loader:当前被代理对象的类加载器
     * Class<?>[] interfaces:当前被代理对象所实现的所有接口
     * InvocationHandler h:被代理对象再执行目标方法时我们使用h可以定义拦截增强方法
     */
    // 获取被代理对象的代理对象
    public static <T> T getProxy(T t) {
        Object o = Proxy.newProxyInstance(
                t.getClass().getClassLoader(),
                t.getClass().getInterfaces(),
                new JdkTikTokProxy(t));
        return (T) o;
    }

    /**
     * 定义目标方法的拦截逻辑:每个方法都会进来的
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 反射执行
        System.out.println("真正执行被代理对象的方法");
        Object invoke = method.invoke(target, args);
        System.out.println("返回值为:" + invoke);
        return invoke;
    }
}

/**
 * 动态代理模式
 * 代理对象和目标对象的相同点时需要实现同一个接口
 */
public class MainTest {
    public static void main(String[] args) {
        ZhuicatTikTok tikTok = new ZhuicatTikTok();
        // new Cat
        ManTikTok proxy = JdkTikTokProxy.getProxy(tikTok);
//        proxy.tikTok();
//        proxy.hashCode();
        ((SellTikTok) proxy).sell();

        // 能不能代理被代理对象奔雷自己的方法?不能,proxy只能转为接口类型
//        ((ZhuicatTikTok) proxy).haha();

        System.out.println(Arrays.asList(proxy.getClass().getInterfaces()));
    }
}
真正执行被代理对象的方法
只要666
返回值为:666
[interface com.xl.web.proxy.synamic.ManTikTok, interface com.xl.web.proxy.synamic.SellTikTok]

缺点:被代理对象必须实现接口才行

🍔Cglib动态代理代码实现

引入依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
public class ZhuicatTikTok  {
    public void tikTok() {
        System.out.println("张三 tiktok 哈哈哈哈哈");
    }

}
/**
 * 使用 cglib 帮我们创建出代理对象
 */
public class CglibProxy {

    // 为任意对象创建代理
    public static <T> T createProxy(T t) {
        // 1、创建一个增强器
        Enhancer enhancer = new Enhancer();
        // 2、设置要增强哪个类的功能。增强类为t类动态创建一个子类
        enhancer.setSuperclass(t.getClass());
        // 3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 拦截并处理被代理对象的方法调用
             * @param o 被代理对象
             * @param method 将要执行的目标方法
             * @param args 目标方法的参数数组
             * @param methodProxy 方法代理对象,用于调用目标方法
             * @return 返回经过拦截处理后的方法结果
             * @throws Throwable 若方法调用过程中出现异常,则抛出
             */
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 在调用实际方法前的操作,例如记录日志、权限校验等
                System.out.println("拦截钱");
                // 通过methodProxy调用原始方法,并获取返回值
                // Object result = methodProxy.invokeSuper(o, args);
                // 或【本质就是执行 目标对象的 方法】
                Object result = method.invoke(t, args);
                // 在调用实际方法后的操作,例如更新数据状态、发送通知等
                System.out.println("拦截后");
                // 返回处理后的结果
                return result;
            }
        });
        return (T) enhancer.create();
    }
}

public class MainTest {
    public static void main(String[] args) {
        ZhuicatTikTok zhuicatTikTok = new ZhuicatTikTok();
        ZhuicatTikTok proxy = CglibProxy.createProxy(zhuicatTikTok);
        proxy.tikTok();
        System.out.println("========");
        proxy.hashCode();
    }
}
拦截钱
张三 tiktok 哈哈哈哈哈
拦截后
========
拦截钱
拦截后

原本的类

cglib代理类 继承了 原本的类

🍕应用场景

  • 什么场景用到?
    • Mybatis 的 mapper 到底是什么?怎么生成的?
      • Mybatis 采用的是 JDK 动态代理
      • UserMapper,Mybatis 帮我们写实现 MapperProxy
    • Seata 的 DataSourceProxy 是什么?
    • DruidDataSource 存在的 Proxy 模式

区别装饰器和代理模式

  • 装饰器和代理之间的区别很细微,可以认为装饰器是代理的一个子集。
  • 静态代理就是装饰器的方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值