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

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

一、关于静态代理

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实时生图绘板
目录
相关文章
|
2月前
|
人工智能
Claude code AI 技能神器:Anthropic Skills!
Anthropic推出Claude AI技能神器Skills,将复杂任务打包成即插即用的“外挂”,让AI秒懂流程,告别重复提示。支持团队共享,提升效率数倍,三步搞定专业报告,堪称AI办公革命!
707 4
|
10月前
|
Java API 数据安全/隐私保护
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
341 0
探索Java动态代理的奥秘:JDK vs CGLIB
|
11月前
|
人工智能 监控 安全
智慧工地解决方案,Spring Cloud智慧工地源代码
智慧工地平台针对建筑工地人员管理难、机械设备繁多、用电安全及施工环境复杂等问题,通过集成应用和硬件设备,实现数据互联互通与集中展示。基于微服务架构(Java+Spring Cloud+UniApp+MySql),平台支持PC端、手机端、平板端、大屏端管理,涵盖人员实名制、工资考勤、视频AI监控、绿色施工、危大工程监测、物料管理和安全质量管理等功能,助力施工现场的数字化、智能化综合管理,提升效率与安全性。
257 15
|
10月前
|
机器学习/深度学习 存储 人工智能
MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
MNN-LLM App 是阿里巴巴基于 MNN-LLM 框架开发的 Android 应用,支持多模态交互、多种主流模型选择、离线运行及性能优化。
7821 80
MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
|
11月前
|
人工智能 自然语言处理 Java
Spring AI,搭建个人AI助手
本期主要是实操性内容,聊聊AI大模型,并使用Spring AI搭建属于自己的AI助手、知识库。本期所需的演示源码笔者托管在Gitee上(https://gitee.com/catoncloud/spring-ai-demo),读者朋友可自行查阅。
3121 44
Spring AI,搭建个人AI助手
|
12月前
|
JSON Java API
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
1043 69
利用Spring Cloud Gateway Predicate优化微服务路由策略
|
10月前
|
Java 关系型数据库 MySQL
SpringBoot 通过集成 Flink CDC 来实时追踪 MySql 数据变动
通过详细的步骤和示例代码,您可以在 SpringBoot 项目中成功集成 Flink CDC,并实时追踪 MySQL 数据库的变动。
2507 45
|
10月前
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
344 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
10月前
|
机器学习/深度学习 人工智能 缓存
探秘 DeepSeek:那些你必须了解的事
DeepSeek是一家由中国幻方量化支持的创新型AI公司,专注于开发高性能、低成本的大语言模型。其独特的技术路径打破了参数规模、能耗成本和认知可靠性之间的“三元悖论”,实现了在单张显卡上运行170亿参数模型的突破。DeepSeek通过开源策略和高性价比模型(如DeepSeek-R1),大幅降低了AI应用门槛,推动了全球开发者社区的发展。其应用场景广泛覆盖教育、医疗、金融等领域,显著提升了工作效率和服务质量。DeepSeek的成功不仅在于技术创新,更在于其开放合作的理念,正引领AI行业的新变革。
1611 9
探秘 DeepSeek:那些你必须了解的事
|
10月前
|
设计模式 SQL 算法
「全网最细 + 实战源码案例」设计模式——模板方法模式
模板方法模式是一种行为型设计模式,定义了算法的骨架并在父类中实现不变部分,将可变部分延迟到子类实现。通过这种方式,它避免了代码重复,提高了复用性和扩展性。具体步骤由抽象类定义,子类实现特定逻辑。适用于框架设计、工作流和相似算法结构的场景。优点包括代码复用和符合开闭原则,缺点是可能违反里氏替换原则且灵活性较低。
254 7
「全网最细 + 实战源码案例」设计模式——模板方法模式