一、关于静态代理
1.1 简介
静态代理是代理模式的一种实现方式,其特点是代理类在编译时就已经确定,代理类的代码是在程序编译阶段生成的,而不是运行时动态生成。
在静态代理中,代理对象和真实对象都需要实现相同的接口。代理类会通过调用真实对象的方法来完成实际的业务操作。代理类和真实类的关系是在编译阶段就已确定,因此也被称为"静态"代理。
代理模式(Proxy Pattern) 是一种结构型设计模式,其核心思想是通过代理对象来间接访问真实对象,从而实现对真实对象的控制和扩展。代理模式通常用于延迟加载、权限控制、日志记录、性能监控等场景。
1.2 发展
- 起源:静态代理随着OOP和设计模式的兴起而出现,用于解决功能扩展和访问控制问题。
- 成熟:在1990s-2000s期间,静态代理被广泛应用于权限控制、日志记录等场景。
- 挑战:随着系统复杂度增加,静态代理的局限性(如代码冗余和灵活性不足)逐渐显现。
- 演进:动态代理技术的出现弥补了静态代理的不足,成为更主流的代理实现方式。
1.3 特点
优点
- 职责清晰:代理类负责控制访问,目标类专注于业务逻辑。
- 扩展性:可以在不修改目标类的情况下增强功能,如日志记录、权限检查等。
- 安全性:通过代理类限制对目标类的直接访问。
缺点
- 代码冗余:每个目标类都需要一个对应的代理类,增加代码量。
- 灵活性不足:代理类与目标类紧密耦合,难以应对复杂需求。
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理(后续分享) !
1.4 核心组成
- 目标类(Real Subject) :实际执行业务逻辑的类。
- 代理类(Proxy) :实现与目标类相同的接口,并持有目标类的引用,控制对目标类的访问。
- 接口(Subject) :定义目标类和代理类的共同行为。
1.5 应用场景
- 需要对目标对象的功能进行增强(如日志记录、权限检查、性能监控等)。
- 目标对象的创建和初始化成本较高,可以通过代理延迟加载。
- 需要对目标对象的访问进行控制(如权限验证)。
二、实现步骤
- 定义一个接口,声明目标类和代理类的共同方法。
- 创建目标类,实现接口并完成核心业务逻辑。
- 创建代理类,实现相同的接口,并在方法调用前后添加额外逻辑。
- 客户端通过代理类访问目标类。
三、实现示例
示例1—权限检查
package org.example;public class StaticProxyDemo {// 1. 定义接口public interface UserService {void addUser(String name);}// 2. 实现目标类(实际执行业务逻辑)public static class UserServiceImpl implements UserService {@Overridepublic 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;}@Overridepublic 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");}}}
代理类代码解释:
-
private UserService userService;
这行代码定义了一个私有变量userService
,其类型是UserService
接口。这个变量的作用是保存对实际执行业务逻辑的对象(也就是被代理对象)的引用。通过这种方式,代理类UserServiceProxy
可以在不改变原有功能的情况下,向原有的方法调用添加额外的行为(如前置操作和后置操作)。 -
public UserServiceProxy(UserService userService)
这是一个构造函数,它接受一个UserService
类型的参数,并将其赋值给实例变量userService
。这意味着当你创建UserServiceProxy
的实例时,你需要传递一个实现了UserService
接口的具体类的实例(即目标对象)。这样做使得UserServiceProxy
能够在其内部使用该目标对象的方法,同时还可以在调用前后插入额外的操作。
输出
前置操作:权限检查
添加用户: Mike
后置操作:日志记录
示例2—日志功能
在增删改业务中增加日志功能
-
创建一个抽象角色,比如我们平时做的用户业务,抽象起来就是增删改查!
// 抽象角色:增删改查业务 public interface UserService { void add(); void delete(); void update(); void query(); }
-
我们需要一个真实对象来完成这些增删改查操作
// 真实对象,完成增删改查操作的人 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("查询了一个用户"); } }
-
需求来了,现在我们需要增加一个日志功能,怎么实现!
- 思路1 :在实现类上增加代码 【麻烦!】
- 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!
-
设置一个代理类来处理日志! 代理角色
// 代理角色,在这里面增加日志的实现 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+"方法"); } }
-
测试访问类:
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中最核心的思想)
静态代理模式以简洁的方式架起了代码复用与功能扩展的桥梁,通过代理对象的“中间层”巧妙实现核心逻辑与附加功能的解耦。虽然其需要手动编写代理类的特性在接口频繁变动时略显笨拙,但在早期设计明确、功能需求稳定的场景中,它依然是轻量级增强代码灵活性的优选方案。
理解静态代理不仅能帮助我们掌握代理模式的核心思想,更是迈向动态代理、AOP等高级技术的重要基石。未来面对复杂场景时,不妨结合其他设计模式,让静态代理在架构设计中绽放更多可能。—— 用模式解决痛点,才是设计的真谛。
✨ 如果本文对你有帮助,欢迎点赞收藏!
📢 你的每一次互动,都是我持续创作的动力!