惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
Recent Announcements
Recent Announcements
H
Help Net Security
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
有赞技术团队
有赞技术团队
小众软件
小众软件
Last Week in AI
Last Week in AI
U
Unit 42
Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
云风的 BLOG
云风的 BLOG
V
V2EX
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)
阮一峰的网络日志
阮一峰的网络日志
爱范儿
爱范儿

牛牛技术客栈

设计模式-备忘录模式 - OXOXTECH 牛牛技术客栈 设计模式-中介者模式 - OXOXTECH 牛牛技术客栈 Linux【Ubuntu】修改ssh默认端口 - OXOXTECH 牛牛技术客栈 设计模式-迭代器模式 - OXOXTECH 牛牛技术客栈 scheduled定时任务的三种基本实现方式 - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(三) - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(二) - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(一) - OXOXTECH 牛牛技术客栈 基于JavaFX的桌面端网络调试工具 - OXOXTECH 牛牛技术客栈 Golang Channel的原理介绍 - OXOXTECH 牛牛技术客栈 Go语言Map的原理分析 - OXOXTECH 牛牛技术客栈 Go语言错误处理(panic)的最佳实践 - OXOXTECH 牛牛技术客栈 设计模式-解释器模式 - OXOXTECH 牛牛技术客栈 Redis报错Redis is configured to save RDB snapshots, but it's currently unable to persist to disk. go-webpbin库在Linux报错failed to encode image to WebP: exit status 1.......的问题 exe4j 打包加密的jar - OXOXTECH 牛牛技术客栈 Go生成图形验证码示例 - OXOXTECH 牛牛技术客栈 澳门一天游:一日尽享东方与西方的交融之美 - OXOXTECH 牛牛技术客栈 设计模式-命令模式 - OXOXTECH 牛牛技术客栈 别再自己瞎写工具类了,SpringBoot内置工具类应有尽有 - OXOXTECH 牛牛技术客栈 中山一日游 - OXOXTECH 牛牛技术客栈 设计模式-责任链模式 - OXOXTECH 牛牛技术客栈 起舞吧,齐舞吧 - OXOXTECH 牛牛技术客栈 设计模式-组合模式 - OXOXTECH 牛牛技术客栈 Go语言Web开发|GoFrame框架入门笔记 - OXOXTECH 牛牛技术客栈 Java打包exe教程 - OXOXTECH 牛牛技术客栈 MySQL存储过程的优缺点有哪些? - OXOXTECH 牛牛技术客栈 前端渲染优化有哪些? - OXOXTECH 牛牛技术客栈 HTTP状态码及其含义 - OXOXTECH 牛牛技术客栈 从浏览器地址栏输入url到显示页面的步骤 - OXOXTECH 牛牛技术客栈
设计模式-代理模式 - OXOXTECH 牛牛技术客栈
Jensen · 2024-07-02 · via 牛牛技术客栈

设计模式-代理模式

Jensen Java Java, 设计模式

前言

Java开发中,设计模式是常用的一种编码方式,合理使用设计模式可以帮助开发人员更快地编写出高效、可靠和可维护的代码。本期将撰写一个关于设计模式的系列文章,总共23种设计模式将以一篇一篇文章讲解,代码笔记已开源至:Gitee点击跳转。在上一篇《Java设计模式(9)享元模式》文章中,我们介绍了享元模式。本文是这个系列的第十篇章,我们将讲解一下代理模式的实现方式、应用场景以及它的用途。

代理模式

202407021520378830.webp

代理模式中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。

代理模式又有两种主要形式:

  • 静态代理:在编译时就已经确定了代理类和被代理类之间的关系,通常需要为每个被代理类都编写一个对应的代理类,并实现相同的接口。 静态代理的优点是简单易懂,缺点是不灵活,代码冗余。
  • 动态代理:在运行时动态生成代理对象,并根据反射机制调用被代理类的方法。 动态代理可以使用Java原生API或者第三方框架来实现,如JDK Proxy、CGLIB、等。

使用场景

  • 当需要在访问一个对象时进行一些控制或额外处理时,比如我想要在调用某个方法之前或之后添加日志、缓存、事务等功能。
  • 控制对原始对象的访问权限,常用于安全控制和权限管理。例如,实现一个文件访问权限控制时。

代码实现

以下将采用银行与代理银行之间的关系简单实现一下。

/**
 * 银行接口
 * @author Jensen
 * @date 2024-03-28
 * */
public interface Bank {
    void deposit(BigDecimal amount);
    void withdraw(BigDecimal amount);
}
/**
 * 实现银行接口
 * @author Jensen
 * @date 2024-03-28
 * */
public class RealBank implements Bank {
    private BigDecimal balance = new BigDecimal("0.00");

    @Override
    public void deposit(BigDecimal amount) {
        balance = balance.add(amount);
        System.out.println("存款操作完成。当前余额:" + balance + "元");
    }

    @Override
    public void withdraw(BigDecimal amount) {
        if (balance.compareTo(amount) >= 0) {
            balance = balance.subtract(amount);
            System.out.println("取款操作完成。当前余额:" + balance + "元");
        } else {
            System.out.println("余额不足。当前余额:" + balance + "元");
        }
    }
}

以上代码我们创建了一个Bank,分别有depositwithdraw,然后有RealBank实现了Bank并对两个方法做了实际处理。接下来我们需要创建代理对象去代理这个RealBank

静态代理

/**
 * 代理银行
 * @author Jensen
 * @date 2024-03-28
 * */
public class ProxyRealBank implements Bank {
    private final RealBank realBank;

    public ProxyRealBank() {
        this.realBank = new RealBank();
    }

    @Override
    public void deposit(BigDecimal amount) {
        realBank.deposit(amount);
    }

    @Override
    public void withdraw(BigDecimal amount) {
        realBank.withdraw(amount);
    }
}

ProxyRealBank 类也实现了 Bank 接口,但它不直接处理depositwithdraw,而是将请求转发给一个内部的 RealBank 对象。充当一个中介角色,并且也可以做一些额外的操作。外部只需要调用它就OK了。

动态代理

/**
 * 代理银行(动态代理)
 * @author Jensen
 * @date 2024-03-28
 * */
public class BankInvocationHandler implements InvocationHandler {
    private final Object obj;

    public BankInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(obj, args);
    }
}

上述代码使用JDK动态代理实现

  • BankInvocationHandler 类实现了 InvocationHandler 接口。这个接口有一个方法 invoke,用于处理对代理实例的方法调用。
  • BankInvocationHandler 的构造方法接受一个对象作为参数(可代理多种对象),这个对象就是实际处理方法调用的对象。在这里我们是 RealBank 类的实例。
  • invoke 方法会在代理实例上调用任何方法时被调用。它接受三个参数:proxy:代理实例、method:被调用的方法、args:方法调用的参数。在 invoke 方法中,实际的方法调用被转发给了 obj 对象(即本文中的 RealBank 类实例)。

测试使用

System.out.println("------------------------------代理模式(静态代理)-----------------------------");
Bank bank = new ProxyRealBank();
// 存款和取款
BigDecimal money1 = new BigDecimal("100.50");
BigDecimal money2 = new BigDecimal("53.20");
BigDecimal money3 = new BigDecimal("47.55");
System.out.println("向银行存款"+money1+"元");
bank.deposit(money1);
System.out.println("向银行取款"+money2+"元");
bank.withdraw(money2);
System.out.println("向银行取款"+money3+"元");
bank.withdraw(money3);
System.out.println("------------------------------代理模式(动态代理)-----------------------------");
RealBank realBank = new RealBank();
BankInvocationHandler handler = new BankInvocationHandler(realBank);
// 使用动态代理创建 Bank 接口的代理对象
Bank bank1 = (Bank) Proxy.newProxyInstance(Bank.class.getClassLoader(),new Class<?>[]{Bank.class},handler);
System.out.println("向银行存款"+money1+"元");
bank1.deposit(money1);
System.out.println("向银行取款"+money2+"元");
bank1.withdraw(money2);
System.out.println("向银行取款"+money3+"元");
bank1.withdraw(money3);

202407021542534297.webp

结语

代理模式 主要解决的是在直接访问某些对象时可能遇到的问题,例如对象创建成本高、需要安全控制或远程访问等。