静态代理模式:代码世界的“替身演员”

简介: 静态代理是代理模式的一种**实现方式**,其特点是**代理类在编译时就已经确定**,代理类的代码是在程序编译阶段生成的,而不是运行时动态生成。

一、关于静态代理

1.1 简介

静态代理是代理模式的一种实现方式,其特点是代理类在编译时就已经确定,代理类的代码是在程序编译阶段生成的,而不是运行时动态生成。

在静态代理中,代理对象和真实对象都需要实现相同的接口。代理类会通过调用真实对象的方法来完成实际的业务操作。代理类和真实类的关系是在编译阶段就已确定,因此也被称为"静态"代理。

3c333c54-d42e-452c-948d-e1dabe314e12

代理模式(Proxy Pattern) 是一种结构型设计模式,其核心思想是通过代理对象来间接访问真实对象,从而实现对真实对象的控制和扩展。代理模式通常用于延迟加载、权限控制、日志记录、性能监控等场景。

1.2 发展

  • 起源:静态代理随着OOP和设计模式的兴起而出现,用于解决功能扩展和访问控制问题。
  • 成熟:在1990s-2000s期间,静态代理被广泛应用于权限控制、日志记录等场景。
  • 挑战:随着系统复杂度增加,静态代理的局限性(如代码冗余和灵活性不足)逐渐显现。
  • 演进:动态代理技术的出现弥补了静态代理的不足,成为更主流的代理实现方式。

1.3 特点

优点

  1. 职责清晰:代理类负责控制访问,目标类专注于业务逻辑。
  2. 扩展性:可以在不修改目标类的情况下增强功能,如日志记录、权限检查等。
  3. 安全性:通过代理类限制对目标类的直接访问。

缺点

  1. 代码冗余:每个目标类都需要一个对应的代理类,增加代码量。
  2. 灵活性不足:代理类与目标类紧密耦合,难以应对复杂需求。

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理(后续分享) !

1.4 核心组成

  1. 目标类(Real Subject) :实际执行业务逻辑的类。
  2. 代理类(Proxy) :实现与目标类相同的接口,并持有目标类的引用,控制对目标类的访问。
  3. 接口(Subject) :定义目标类和代理类的共同行为。

1.5 应用场景

  • 需要对目标对象的功能进行增强(如日志记录、权限检查、性能监控等)。
  • 目标对象的创建和初始化成本较高,可以通过代理延迟加载。
  • 需要对目标对象的访问进行控制(如权限验证)。

二、实现步骤

  1. 定义一个接口,声明目标类和代理类的共同方法。
  2. 创建目标类,实现接口并完成核心业务逻辑。
  3. 创建代理类,实现相同的接口,并在方法调用前后添加额外逻辑。
  4. 客户端通过代理类访问目标类。

三、实现示例

示例1—权限检查

package org.example;

public class StaticProxyDemo {
   

    // 1. 定义接口
    public interface UserService {
   
        void addUser(String name);
    }

    // 2. 实现目标类(实际执行业务逻辑)
    public static class UserServiceImpl implements UserService {
   
        @Override
        public void addUser(String name) {
   
            System.out.println("添加用户: " + name);
        }
    }

    // 3. 创建代理类
    public static class UserServiceProxy implements UserService{
   
        // 持有目标对象的引用
        private UserService userService;

        public UserServiceProxy(UserService userService){
   
            this.userService=userService;
        }

        @Override
        public void addUser(String name) {
   
            System.out.println("前置操作:权限检查");
            // 调用目标对象的方法
            userService.addUser(name);
            System.out.println("后置操作:日志记录");
        }
    }

    // 4. 使用代理类
    public static class Main{
   
        public static void main(String[] args) {
   
            // 创建目标对象
            UserServiceImpl userService = new UserServiceImpl();

            // 创建代理对象,并传入目标对象
            UserServiceProxy proxy  = new UserServiceProxy(userService);

            // 通过代理对象调用方法
            proxy.addUser("Mike");
        }
    }

}

代理类代码解释:

  1. private UserService userService;
    这行代码定义了一个私有变量 userService​,其类型是 UserService​ 接口。这个变量的作用是保存对实际执行业务逻辑的对象(也就是被代理对象)的引用。通过这种方式,代理类 UserServiceProxy​ 可以在不改变原有功能的情况下,向原有的方法调用添加额外的行为(如前置操作和后置操作)。
  2. public UserServiceProxy(UserService userService)
    这是一个构造函数,它接受一个 UserService​ 类型的参数,并将其赋值给实例变量 userService​。这意味着当你创建 UserServiceProxy​ 的实例时,你需要传递一个实现了 UserService​ 接口的具体类的实例(即目标对象)。这样做使得 UserServiceProxy​ 能够在其内部使用该目标对象的方法,同时还可以在调用前后插入额外的操作。

输出

前置操作:权限检查
添加用户: Mike
后置操作:日志记录

image

示例2—日志功能

在增删改业务中增加日志功能

  1. 创建一个抽象角色,比如我们平时做的用户业务,抽象起来就是增删改查!

    // 抽象角色:增删改查业务
    public interface UserService {
           
        void add();  
        void delete();  
        void update();  
        void query();
    }
    
  2. 我们需要一个真实对象来完成这些增删改查操作

    // 真实对象,完成增删改查操作的人
    public class UserServiceImpl implements UserService {
           
        public void add() {
           
            System.out.println("增加了一个用户");  
        }  
        public void delete() {
           
            System.out.println("删除了一个用户");  
        }  
        public void update() {
           
            System.out.println("更新了一个用户");  
        }  
        public void query() {
           
            System.out.println("查询了一个用户");  
        }
    }
    
  3. 需求来了,现在我们需要增加一个日志功能,怎么实现!

    • 思路1 :在实现类上增加代码 【麻烦!】
    • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!
  4. 设置一个代理类来处理日志! 代理角色

    // 代理角色,在这里面增加日志的实现
    public class UserServiceProxy implements UserService {
           
        private UserServiceImpl userService;  
        public void setUserService(UserServiceImpl userService) {
           
            this.userService = userService;  
        }  
        public void add() {
           
            log("add");  
            userService.add();  
        }  
    
        public void delete() {
           
            log("delete");  
            userService.delete();  
        }  
    
        public void update() {
           
            log("update");  
            userService.update();  
    
        }  
    
        public void query() {
           
            log("query");  
            userService.query();  
        }  
    
        public void log(String msg){
           
            System.out.println("执行了"+msg+"方法");  
        }
    }
    
  5. 测试访问类:

    public class Client {
           
        public static void main(String[] args) {
           
            //真实业务  
            UserServiceImpl userService = new UserServiceImpl();  
            //代理类  
            UserServiceProxy proxy = new UserServiceProxy();  
            //使用代理类实现日志功能!  
            proxy.setUserService(userService);  
            proxy.add();  
        }
    }
    

OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;(在不改变原来的代码的情况下,实现了对原有功能的增强,这是Spring AOP中最核心的思想)

image

image

image

静态代理模式以简洁的方式架起了代码复用与功能扩展的桥梁,通过代理对象的“中间层”巧妙实现核心逻辑与附加功能的解耦。虽然其需要手动编写代理类的特性在接口频繁变动时略显笨拙,但在早期设计明确、功能需求稳定的场景中,它依然是轻量级增强代码灵活性的优选方案。

理解静态代理不仅能帮助我们掌握代理模式的核心思想,更是迈向动态代理、AOP等高级技术的重要基石。未来面对复杂场景时,不妨结合其他设计模式,让静态代理在架构设计中绽放更多可能。—— 用模式解决痛点,才是设计的真谛。

如果本文对你有帮助,欢迎点赞收藏!

📢 你的每一次互动,都是我持续创作的动力!

点击关注收藏

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
9月前
|
JSON API 开发者
Django集成Swagger全指南:两种实用方案详解
本文介绍了在 Django 项目中集成 Swagger 的两种主流方案 —— drf-yasg 和 drf-spectacular,涵盖安装配置、效果展示及高级用法,助力开发者高效构建交互式 API 文档系统,提升前后端协作效率。
392 5
|
4月前
|
缓存 NoSQL 关系型数据库
【高并发实战】Redis缓存穿透、击穿、雪崩:3大经典的“炸库”危机与自救指南
本文详解缓存穿透、击穿、雪崩三大问题:穿透是查不存在的数据,击穿是热点Key失效被高并发冲击,雪崩是大量Key同时过期或Redis故障。结合比喻与解决方案,助你彻底理解并防范数据库风险。
|
机器学习/深度学习 Python 数据处理
Python中利用长短期记忆模型LSTM进行时间序列预测分析 - 预测电力负荷数据
Python中利用长短期记忆模型LSTM进行时间序列预测分析 - 预测电力负荷数据
Python中利用长短期记忆模型LSTM进行时间序列预测分析 - 预测电力负荷数据
|
并行计算 Ubuntu Linux
Ubuntu 20.04 Anaconda的简单使用以及虚拟环境中编译使用 OpenCV 4.11源码
Ubuntu 20.04 Anaconda的简单使用以及虚拟环境中编译使用 OpenCV 4.11源码
1222 0
|
Web App开发 JavaScript 前端开发
Vue系列教程(04)- VsCode断点调试(Debugger for Chrome)
Vue系列教程(04)- VsCode断点调试(Debugger for Chrome)
851 0
|
设计模式 网络协议 Java
06.动态代理设计模式
本文详细介绍了动态代理设计模式,包括其必要性、概念、实现方式及案例分析。动态代理允许在运行时动态创建代理对象,增强代码复用性和灵活性,减少类膨胀。文章通过对比静态代理,深入解析了动态代理的实现机制,如基于接口和类的动态代理,以及其在Retrofit中的应用。同时,讨论了动态代理的优势和潜在问题,如性能开销和调试难度。最后,提供了丰富的学习资源链接,帮助读者进一步理解和掌握动态代理。
266 1
|
运维 Cloud Native 测试技术
极氪汽车 APP 系统云原生架构转型实践
极氪汽车 APP 系统云原生架构转型实践
2784 107
极氪汽车 APP 系统云原生架构转型实践
|
前端开发
CSS动画新潮流:炫酷水波效果,让网页元素生动起来!
CSS动画新潮流:炫酷水波效果,让网页元素生动起来!
|
Java API
什么是Java函数式接口?
【4月更文挑战第13天】
351 0
什么是Java函数式接口?
|
XML Java 数据格式
SpringBoot详解
SpringBoot详解
506 0