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

一、关于静态代理

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

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

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

点击关注收藏
posted @   ccm03  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示