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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 静态代理是代理模式的一种**实现方式**,其特点是**代理类在编译时就已经确定**,代理类的代码是在程序编译阶段生成的,而不是运行时动态生成。

一、关于静态代理

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等高级技术的重要基石。未来面对复杂场景时,不妨结合其他设计模式,让静态代理在架构设计中绽放更多可能。—— 用模式解决痛点,才是设计的真谛。

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

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

点击关注收藏

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
9月前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~结构型]对象的间接访问——代理模式
[设计模式Java实现附plantuml源码~结构型]对象的间接访问——代理模式
|
9月前
|
Java
代码的魔法师:Java反射工厂模式详解
代码的魔法师:Java反射工厂模式详解
75 0
|
9月前
|
设计模式 存储 缓存
二十三种设计模式全面解析-探索解释器模式的高级应用和优化技巧:解锁代码解析的新境界
二十三种设计模式全面解析-探索解释器模式的高级应用和优化技巧:解锁代码解析的新境界
静态代理模式
静态代理模式
72 0
|
安全 Java 编译器
深挖⾯向对象编程三⼤特性 --封装、继承、多态
深挖⾯向对象编程三⼤特性 --封装、继承、多态
深挖⾯向对象编程三⼤特性 --封装、继承、多态
|
Java C++
多重继承有时候确实有必要
多重继承有时候确实有必要
52 0
|
设计模式 存储 JavaScript
白话讲解创建型设计模式:单例、原型,构建
关于设计模式,个人觉得,在理解上要站在问题域的角度,而不是它的实现方式,因为学完全部的设计模式,你会感觉,好像大多设计模式实现上基本一样。往往有这一种被欺骗的感觉....哈
174 1
|
设计模式 安全
单例设计模式的两种方式
单例设计模式的两种方式
151 0
|
设计模式 Java
【Java设计模式】今天终于弄懂了依赖倒转原则和依赖关系传递的三种方式(代码详解)
【Java设计模式】今天终于弄懂了依赖倒转原则和依赖关系传递的三种方式(代码详解)
【Java设计模式】今天终于弄懂了依赖倒转原则和依赖关系传递的三种方式(代码详解)