1、设计模式
一、设计模式:
设计模式的系统化理论,源自1994年《设计模式:可复用面向对象软件的基础》一书,书中首次归纳了23种经典设计模式,奠定了设计模式的理论框架。
设计模式是软件开发中针对通用、高频业务或技术问题的可复用、标准化解决方案,是资深开发者沉淀的编码经验总结,它并非固定可复制的代码片段,而是一套经过验证的编码思想与设计套路,其核心价值在于让代码实现高内聚、低耦合的架构特性,既能显著提升代码的扩展性、可读性与维护性,又能从根源上规避硬编码、冗余代码、过度耦合等开发痛点,从而更好地适配业务的快速迭代与团队的高效协作。
核心注意事项:
- 设计模式的运用必须贴合实际场景,切忌为用而用,过度设计反而会增加代码复杂度与维护成本,违背其设计初衷。
二、七大核心设计原则:
所有设计模式均是七大核心原则的具体落地,掌握原则才能真正理解模式的设计初衷:
1、单一职责原则:
(1)、核心思想:
一个类/方法只承担一个职责,仅存在一个引起它变更的原因。
(2)、核心作用:
降低代码复杂度,提升可读性与维护性。
(3)、简单示例:
- 反例(违背原则)
// 反例(违背原则) // 一个类承担2个职责:计算+打印,两个变更原因 class Demo { // 职责1:做加法计算 public int add(int a, int b) { return a + b; } // 职责2:打印内容 public void print(int num) { System.out.println(num); } }
说明:修改打印格式、修改计算规则,都会改动这个类,存在两个变更原因。
- 正例(遵守原则)
// 正例(遵守原则) // 类1:仅负责计算,只有「计算规则变更」一个修改原因 class Calc { public int add(int a, int b) { return a + b; } } // 类2:仅负责打印,只有「打印格式变更」一个修改原因 class Print { public void show(int num) { System.out.println(num); } }
说明:计算和打印各司其职,修改其中一个,完全不影响另一个类。
2、开闭原则:
(1)、核心思想:
对扩展开放,对修改关闭。
(2)、核心作用:
新增功能不改动原有代码,规避修改引发的连锁问题,提升系统稳定性。
(3)简单示例:
// 基础类:固定不变 class Shape { void draw(){} } // 已实现的图形:无需修改 class Square extends Shape { @Override void draw() { System.out.println("画正方形"); } } // 新增图形:直接扩展,不改任何老代码 class Circle extends Shape { @Override void draw() { System.out.println("画圆形"); } }
说明:加新图形,只加新类,原有代码一行不动。
3、里氏替换原则:
(1)、核心思想:
子类可完全替换父类,程序运行不受任何影响。简单的说就是,子类可以扩展父类的功能,但不能改变父类原有的功能。
(2)、核心作用:
保证继承关系的合理性,避免子类破坏父类原有功能逻辑。
(3)、简单示例:
// 父类 class Phone { void call() { System.out.println("拨打电话"); } } // 子类 class HuaWei extends Phone { @Override void call() { System.out.println("华为打电话"); } } // 测试:子类替换父类,正常运行 public class Test { public static void main(String[] args) { Phone p = new HuaWei(); p.call(); // 替换后功能正常,符合原则 } }
说明:子类替换父类对象,核心功能可正常执行。
4、依赖倒置原则:
(1)、核心思想:
高层模块依赖抽象,不依赖具体实现;面向接口编程,而非面向实现编程。
(2)、核心作用:
降低模块间耦合度,新增实现类无需改动业务代码。
(3)、简单示例:
// 抽象接口(依赖的核心) interface Eat { void eatFood(); } // 具体实现1 class Rice implements Eat { @Override public void eatFood() { System.out.println("吃米饭"); } } // 具体实现2 class Noodle implements Eat { @Override public void eatFood() { System.out.println("吃面条"); } } // 业务类:只依赖接口,不关心具体实现 class Person { void eat(Eat e) { e.eatFood(); } } // 测试 public class Test { public static void main(String[] args) { Person p = new Person(); // 传入米饭,调用吃饭 p.eat(new Rice()); // 传入面条,调用吃饭 p.eat(new Noodle()); } }
说明:加「吃面包」功能,仅新增面包类实现 Eat 接口即可,Person 类无需修改。
5、接口隔离原则:
(1)、核心思想:
拆分臃肿大接口为多个职责单一的小接口,客户端不依赖自身不需要的接口。
(2)、核心作用:
避免冗余代码,实现类仅需实现自身所需方法,提升代码整洁度。
(3)、简单示例:
// 拆分为小接口,职责单一 interface Fly { void fly(); } interface Run { void run(); } // 只实现需要的接口 class Bird implements Fly,Run { public void fly() {System.out.println("飞");} public void run() {System.out.println("跑");} } class Dog implements Run { public void run() {System.out.println("跑");} }
说明:Dog 无需实现无关的 fly 方法,只做自己需要的事。
6、迪米特法则(最少知道原则):
(1)、核心思想:
一个对象只与直接关联对象通信,不窥探其他对象的内部细节。
(2)、核心作用:
减少对象间不必要交互,降低耦合,提升模块独立性。
(3)、简单示例:
class Book { private int page = 200; // 对外提供获取方法,不暴露属性 public int getPage() { return page; } } class Reader { // 只调用公开方法,不直接访问属性 void read(Book b) { System.out.println("共" + b.getPage() + "页"); } }
说明:Reader 只调用 Book 的公开方法,不直接操作 b.page,不关注内部细节。
7、合成复用原则:
(1)、核心思想:
优先使用组合/聚合实现功能复用,而非继承。
(2)、核心作用:
避免继承的强耦合问题,功能扩展更灵活,可动态替换复用的功能。
(3)、简单示例:
// 待复用的功能类 class Music { void play() { System.out.println("播放音乐"); } } // 组合复用,而非继承 class Phone { Music m = new Music(); // 组合 void use() { m.play(); // 调用复用功能 System.out.println("使用手机"); } }
说明:想换音乐功能,直接替换 Music 对象即可,无需修改 Phone 类结构。
三、设计模式的类型:
设计模式体系可划分为三大核心类别,各类别定位与具体内容明确区分:
- 一是经典基础设计模式,总计23种,是行业公认的标准核心,细分为创建型(5种)、结构型(7种)、行为型(11种),聚焦解决代码层对象创建、类与对象组合、对象间协作的基础开发问题;
- 二是并发型设计模式,以生产者 - 消费者模式、线程池模式为核心,专门解决多线程高并发场景下的任务调度、资源竞争与性能优化问题;
- 三是架构型设计模式,主流包含 MVC 模式、微服务模式,面向系统宏观设计层面,解决项目分层、服务拆分、整体架构规划的顶层设计问题。
三类模式各有侧重、互为补充,共同构成覆盖代码编码、并发开发、系统架构全场景的完整设计模式体系。
1、经典基础设计模式-创建型模式:
核心:解决「对象创建」问题,封装对象实例化逻辑,解耦创建与使用,控制对象生成方式。
|
模式名称 |
描述 |
|
单例模式 |
保证一个类全局仅存在一个实例,避免重复创建浪费资源,统一访问入口 |
|
工厂方法模式 |
定义创建对象的接口,由子类决定具体创建哪种对象,统一管理对象创建逻辑 |
|
抽象工厂模式 |
创建一系列相关/依赖的对象簇,无需指定具体类,批量管理同类产品创建 |
|
建造者模式 |
分步构建复杂对象,将对象创建与组装分离,按需灵活配置对象属性 |
|
原型模式 |
通过复制已有对象(克隆) 创建新对象,避免重复执行初始化逻辑,提升创建效率 |
2、经典基础设计模式-结构型模式:
核心:解决「类 / 对象组合」问题,优化类与对象的结构关系,实现功能扩展、解耦适配,不修改原有代码。
|
模式名称 |
描述 |
|
代理模式 |
为对象提供代理类,统一处理附加逻辑(日志、权限),不侵入原对象核心代码 |
|
装饰器模式 |
动态为对象新增功能,多层嵌套扩展,不改变原对象结构 |
|
适配器模式 |
适配接口不兼容的类,让原本无法协作的类可以一起工作(类似转接头) |
|
桥接模式 |
将抽象与实现分离,二者独立变化,解耦多维度的类结构扩展 |
|
组合模式 |
统一处理单个对象与对象集合,树形结构场景下,一致对待所有节点 |
|
外观模式 (门面模式) |
提供统一入口,封装复杂子系统调用,简化外部与系统的交互 |
|
享元模式 |
复用内存中已存在的对象,减少重复创建,节省内存开销 |
3、经典基础设计模式-行为型模式:
核心:解决「对象间通信与职责协作」问题,规范对象交互规则、分配行为职责,让协作更清晰可控。
|
模式名称 |
描述 |
|
策略模式 |
封装不同业务策略,可动态切换,修改策略不影响核心业务逻辑 |
|
观察者模式 |
一对多联动,被观察者状态变更时,所有依赖的观察者自动更新(订阅/通知) |
|
模板方法模式 |
固定流程骨架,子类仅实现流程中的具体细节步骤,统一执行规范 |
|
迭代器模式 |
统一遍历容器对象的方式,屏蔽容器内部数据结构差异 |
|
命令模式 |
将请求封装为对象,实现请求发起者与执行者解耦,支持请求排队/撤销 |
|
备忘录模式 |
保存对象的历史状态,支持对象恢复到指定历史节点,不暴露内部细节 |
|
状态模式 |
封装对象不同状态及对应行为,状态变更时自动切换执行逻辑 |
|
访问者模式 |
为对象结构中的元素,提供多种不同的访问/操作方式,解耦数据与行为 |
|
中介者模式 |
统一管理对象间的交互,所有对象通过中介者通信,减少直接依赖 |
|
解释器模式 |
定义语言文法规则,解析并执行自定义的简单语言表达式 |
|
职责链模式 (责任链模式) |
构建请求处理链条,请求沿链传递,由对应节点处理,解耦请求与处理 |
4、并发型模式:
核心:解决多线程并发编程中的资源竞争、任务调度、效率优化问题,保证并发安全与性能,是开发高并发系统的必备模式
|
模式名称 |
描述 |
|
生产者 - 消费者模式 |
解耦「数据生产」与「数据消费」,通过缓冲区隔离二者,平衡生产 / 消费速度,避免线程阻塞、资源竞争 |
|
线程池模式 |
预先创建一批核心线程并统一管理,任务复用线程执行,避免频繁创建 / 销毁线程的性能损耗,管控并发数、统一调度任务 |
5、架构型模式:
核心:解决系统整体架构设计层面的问题,定义系统分层、模块划分、服务拆分的通用规范,适配大型项目的架构规划,区别于代码级的经典模式(更偏向宏观设计思想)
|
模式名称 |
描述 |
|
MVC模式 |
核心分层设计:Model(数据层)、View(视图层)、Controller(控制层),分离数据、界面、业务逻辑,降低模块耦合,适配前端 / 后端开发 |
|
微服务模式 |
将单体系统拆分为独立、自治的小服务,服务间轻量通信,单独部署、独立迭代,解决单体系统臃肿、扩展困难的问题,适配大型分布式系统 |
四、创建型模式—单例模式:
1、核心思想:
单例模式是指在内存中仅会创建一次对象的设计模式,确保一个类的实例在应用程序中全局唯一,并提供统一的访问入口。
|
类型 |
说明 |
|
饿汉式 |
类加载阶段就完成实例初始化,依托 JVM 类加载机制天然线程安全,但无懒加载特性,易造成早期资源占用; |
|
懒汉式 |
遵循懒加载思想,仅首次调用访问方法时创建实例,基础版无同步措施线程不安全,可通过双重检查锁 + volatile 优化为线程安全的高性能版本; |
|
静态内部类 |
融合饿汉式线程安全与懒汉式懒加载优势,利用 JVM 对静态内部类的独立加载规则实现按需初始化,无需手动加锁,是日常开发优选; |
|
枚举式 |
Java 语法层面最优解,依托枚举原生特性天然线程安全,且能从根源抵御反射、序列化对单例的破坏,安全性最高; |
2、核心作用:
(1)、节省内存资源,避免频繁创建与销毁同一类对象带来的性能损耗;
(2)、统一管理全局共享资源(如配置文件、工具类、数据库连接池),保障数据操作的一致性;
(3)、严格控制实例的创建逻辑,规避多实例共存引发的业务逻辑冲突。
类型一:饿汉式创建单例对象
(1)、在类加载阶段就完成单例对象的初始化,对象随类加载就绪,等待程序调用,不存在懒加载特性。
(2)、通过static final关键字修饰实例变量实现,基于JVM类加载机制天然具备线程安全特性。
// 饿汉式单例标准实现(类加载初始化,天然线程安全) public class Singleton { // 类加载时初始化唯一实例,static保证全局共享,final保证实例不可被篡改,杜绝二次赋值 private static final Singleton singleton = new Singleton(); // 私有化构造方法,完全禁止外部通过new关键字创建本类实例 private Singleton(){} // 对外暴露全局唯一的实例访问入口,直接返回已初始化的单例对象 public static Singleton getInstance() { return singleton; } } // 饿汉式单例调用示例,全局统一方式获取实例并验证单例特性 class Client { public static void main(String[] args) { // 通过类名调用静态方法,获取全局唯一的单例对象 Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); // 验证单例特性:多次获取的引用指向同一个对象 System.out.println(instance1 == instance2); // 结果为true,证明实例全局唯一 } }
类型二:懒汉式创建单例对象
(1)、遵循懒加载设计思想,仅在真正需要使用对象时才去创建该单例类对象,避免类加载阶段的无效资源占用。
(2)、通常说的“懒汉式单例”指未加锁的基础版本,基础懒汉式单例虽实现了懒加载(仅调用getInstance()时创建实例),但未做线程同步,多线程同时进入判空逻辑会创建多个实例,无法保证单例唯一性,存在线程安全问题。
// 懒汉式单例基础版(未加锁,仅实现懒加载,多线程环境下线程不安全) public class LazySingleton { // 1. 私有静态变量:仅声明不初始化,实现懒加载的核心 private static LazySingleton singleton; // 2. 私有化构造方法,禁止外部通过new创建对象 private LazySingleton(){} // 3. 公共静态方法:仅在调用时判空创建实例,无任何线程同步措施 public static LazySingleton getInstance() { // 仅单层判空:首次调用时创建实例,实现懒加载 // 多线程并发时,多个线程可同时进入此判空逻辑,导致创建多个实例 if (singleton == null) { singleton = new LazySingleton(); } return singleton; } // 调用示例:验证单线程下的懒加载特性 public static void main(String[] args) { // 首次调用getInstance(),触发实例创建(懒加载) LazySingleton instance1 = LazySingleton.getInstance(); // 第二次调用,直接返回已创建的实例 LazySingleton instance2 = LazySingleton.getInstance(); // 单线程环境下:验证引用指向同一个对象(结果为true) System.out.println("单线程下实例是否唯一:" + (instance1 == instance2)); } }
(3)、通过双重检查锁校验保证多线程环境下的实例唯一性,结合volatile关键字防止指令重排,是生产环境中主流的懒加载单例实现方案。
// 懒汉式单例标准实现(双重检查锁+volatile,懒加载+线程安全) public class Singleton { // volatile关键字:禁止指令重排,确保实例完成完整初始化后,再被其他线程读取使用 private static volatile Singleton singleton; // 私有化构造方法,禁止外部主动创建对象 private Singleton(){} public static Singleton getInstance() { // 第一次判空:跳过同步代码块,提升日常调用的执行效率,避免锁竞争损耗 if (singleton == null) { // 加类对象锁:保证多线程并发时,仅有一个线程能进入实例创建逻辑 synchronized(Singleton.class) { // 第二次判空:防止多个线程等待锁后,重复执行实例创建操作 if (singleton == null) { singleton = new Singleton(); } } } // 返回全局唯一的单例实例 return singleton; } } // 懒汉式单例调用示例,全局统一方式获取实例并验证单例特性 class Client { public static void main(String[] args) { // 通过类名调用静态方法,获取全局唯一的单例对象 Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); // 验证单例特性:多次获取的引用指向同一个对象 System.out.println(instance1 == instance2); // 结果为true,证明实例全局唯一 } }
类型三:静态内部类创建单例对象
(1)、融合饿汉式的线程安全性与懒汉式的懒加载优势,是日常开发中平衡性能与资源占用的优选方案;利用 JVM 类加载机制,外部类加载时静态内部类不会被初始化,仅在首次调用自定义的单例全局访问方法时,才加载内部类并创建单例实例,真正实现 “按需创建”。
(2)、依托 JVM 原生的类加载线程安全保障,无需手动加锁或使用 volatile 关键字,既避免了饿汉式类加载阶段的无效资源占用,又解决了基础懒汉式的线程安全问题,实现逻辑简洁且无性能损耗。
(3)、存在少量安全局限:正常场景下可保证单例唯一性,但无法像枚举那样从语法层面抵御反射、序列化对单例的破坏。
// 静态内部类实现单例模式(兼顾懒加载+线程安全,无性能损耗) public class StaticInnerClassSingleton { // 1. 私有化构造方法:禁止外部通过new创建对象 private StaticInnerClassSingleton() { // 可选:防止反射破坏(日常开发可省略,枚举能从根源解决) if (SingletonHolder.INSTANCE != null) { throw new RuntimeException("禁止重复创建单例实例"); } } // 2. 静态内部类:仅在被访问时才会加载,实现懒加载 // JVM保证内部类的加载过程线程安全,且仅加载一次 private static class SingletonHolder { // 3. 内部类的静态常量:初始化唯一的单例实例 private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton(); } // 4. 公共静态方法(单例全局访问方法):提供全局访问入口,调用时触发内部类加载 public static StaticInnerClassSingleton getInstance() { return SingletonHolder.INSTANCE; } // 调用示例:验证单例特性 public static void main(String[] args) { // 首次调用getInstance():触发SingletonHolder加载,创建实例(懒加载) StaticInnerClassSingleton instance1 = StaticInnerClassSingleton.getInstance(); // 第二次调用:直接返回已创建的实例,无新实例创建 StaticInnerClassSingleton instance2 = StaticInnerClassSingleton.getInstance(); // 验证单例特性:多次获取的引用指向同一个对象 System.out.println("实例是否唯一:" + (instance1 == instance2)); // 输出true // 多线程验证(可选):即使多线程并发调用,也只会创建一个实例 new Thread(() -> System.out.println(StaticInnerClassSingleton.getInstance())).start(); new Thread(() -> System.out.println(StaticInnerClassSingleton.getInstance())).start(); // 控制台输出的实例地址一致,证明线程安全 } }
注:
饿汉式static final实例与静态内部类static变量初始化时机不同的核心原因,是 JVM 对外部类静态成员和静态内部类的加载、初始化规则存在差异,具体来说,JVM 遵循两条关键规则:一是类仅在首次主动使用(调用静态方法、访问非编译期常量的静态变量、创建实例、反射调用等)时才会被加载;二是类加载完成后会进入初始化阶段,此时该类的所有静态成员会按声明顺序一次性初始化且仅初始化一次,基于这两条规则,饿汉式的static final实例属于外部类的静态成员,当外部类被首次主动使用而加载时,这个实例就会随之完成初始化,因此不具备懒加载特性,而静态内部类是独立于外部类的类,外部类加载时并不会触发静态内部类的加载,只有当调用自定义的单例全局访问方法去访问静态内部类的静态变量时,静态内部类才会被首次主动使用并加载,进而完成实例初始化,最终实现懒加载。
类型四:枚举创建单例对象
饿汉式与懒汉式的单例实现,均存在被外部手段破坏的风险。反射机制可通过暴力调用类的私有构造器执行newInstance ()方法,强行创建新的实例;序列化与反序列化操作,在读取对象时也会生成全新的实例对象,这两种方式都会直接打破单例模式的核心规则。枚举实现单例是Java语言层面提供的最优解,依托枚举类的原生底层特性,可从根源上解决上述问题,同时天然满足线程安全要求,是目前业界公认的最安全、最规范、最优雅的单例实现方式。
(1)、基于Java枚举类的语法特性实现单例,枚举类的实例由JVM负责统一管理与初始化,全局仅能存在一个枚举实例,无法被外部篡改,保证了初始化过程的线程安全性。
(2)、写法简洁且无需额外做安全校验,可直接在枚举类中声明业务属性与方法,完美适配实际开发中的工具类、配置管理类等场景。
// 枚举单例标准落地实现(声明全局唯一实例,集成业务功能) public class Client { public enum ConfigManager { // 定义枚举类的唯一实例,由JVM保证全局唯一,是枚举单例的核心标识 INSTANCE; // 私有成员变量,用于存储全局配置数据,按需自定义业务属性 private Map<String, String> configMap; // 懒加载初始化配置,保证配置数据按需加载,避免启动时的资源浪费 public void loadConfig() { if (configMap == null) { configMap = new HashMap<>(); // 模拟项目全局配置加载逻辑,可替换为真实配置读取操作 configMap.put("name", "single-instance-demo"); } } // 对外提供统一的配置获取方法,封装配置读取逻辑 public String getConfig(String key) { loadConfig(); return configMap.get(key); } } // 枚举单例调用示例,全局统一方式获取实例并执行业务操作 public static void main(String[] args) { // 直接通过「枚举类名.实例名」获取全局唯一的单例对象 ConfigManager config = ConfigManager.INSTANCE; // 调用枚举实例的业务方法,获取指定配置信息 System.out.println(config.getConfig("name")); // 验证单例特性:多次获取的引用指向同一个对象 ConfigManager config2 = ConfigManager.INSTANCE; System.out.println(config == config2); // 结果为true,证明实例全局唯一 } }
枚举单例核心安全特性说明:
- 防反射破坏:Java 反射机制在检测到要通过 newInstance () 创建枚举类实例时,会直接抛出 IllegalArgumentException 异常,从语法层面禁止反射创建枚举实例,彻底杜绝反射对单例的破坏;而饿汉式、懒汉式的私有构造器,可被反射暴力破解并生成新实例。
- 防序列化破坏:枚举类的序列化与反序列化过程由 JVM 原生管控,反序列化时不会创建新的对象实例,始终返回枚举类中定义的唯一 INSTANCE;而饿汉式、懒汉式实现序列化接口后,反序列化操作会生成全新对象,直接打破单例约束。
- 天然线程安全:枚举类的唯一实例在类加载阶段由 JVM 完成初始化,JVM 的类加载机制本身保证了初始化过程的线程安全性,无需额外加锁、无需使用 volatile 关键字,对比懒汉式更简洁,对比饿汉式安全性更有保障。
五、创建型模式—工厂模式:
1、核心思想:
工厂模式是一套封装对象创建逻辑的设计模式,核心是将「对象的创建逻辑」与「对象的业务使用逻辑」解耦,不再直接通过new关键字硬编码创建对象,而是交由专门的 “工厂” 类 / 接口统一负责对象创建,降低代码耦合度,提升系统的扩展性和可维护性。
|
类型 |
说明 |
|
简单工厂 |
1、由一个工厂类的静态方法根据入参创建不同产品实例,实现创建逻辑集中管理,但新增产品需修改工厂类代码,违背 “开闭原则”; 2、单一工厂造所有产品 |
|
工厂方法 |
1、定义创建对象的抽象工厂接口,由具体子类工厂实现对应产品的创建逻辑(一个产品对应一个工厂子类),符合开闭原则,但会导致系统类数量增多; 2、一个产品一个工厂 |
|
抽象工厂 |
1、围绕 “超级工厂” 创建多个产品族的产品(如创建 “手机 + 耳机” 一套电子产品),可批量创建一组相关联 / 相依赖的产品,适配多产品族、多维度扩展的场景,抽象度和扩展性更强; 2、一个工厂造一组产品 |
2、核心作用:
(1)、解耦对象创建与使用,屏蔽产品创建的复杂细节,降低调用方使用成本;
(2)、统一管理对象创建逻辑,将分散的 new 对象操作集中,便于后续维护与修改;
(3)、符合开闭原则,可灵活扩展产品类型,不侵入原有业务代码;
(4)、提升代码复用性与可读性,规避重复的对象初始化代码。
类型一:简单工厂模式(静态工厂模式)
(1)、核心特点:无抽象工厂层,由一个具体工厂类包揽所有产品的创建逻辑,通过参数判断、动态返回不同类型的产品对象,是最简洁、最易用的工厂实现方式,不属于 GoF23 种标准模式,却是工程开发中高频使用的基础写法。
(2)、单工厂、多产品,无抽象层,写法极简但扩展需修改工厂代码,适合简单场景。
// 简单工厂模式标准实现 // 1. 抽象产品:定义所有产品的统一功能规范 public interface Product { void produce(); } // 2. 具体产品1:手机产品,实现抽象产品规范 class Phone implements Product { @Override public void produce() { System.out.println("生产手机产品"); } } // 3. 具体产品2:电脑产品,实现抽象产品规范 class Computer implements Product { @Override public void produce() { System.out.println("生产电脑产品"); } } // 4. 核心:简单工厂类,统一创建所有产品,包揽全部创建逻辑 class ProductFactory { // 静态方法创建产品,传入类型参数即可获取对应产品,调用极简 public static Product createProduct(String productType) { if ("phone".equals(productType)) { return new Phone(); } else if ("computer".equals(productType)) { return new Computer(); } throw new IllegalArgumentException("暂不支持该产品类型"); } } // 简单工厂模式调用示例,无需关心创建细节,传参即可获取产品 class Client { public static void main(String[] args) { // 调用工厂静态方法,传入类型获取产品对象 Product phone = ProductFactory.createProduct("phone"); Product computer = ProductFactory.createProduct("computer"); // 直接调用产品业务方法 phone.produce(); computer.produce(); } }
类型二:工厂方法模式
(1)、核心特点:基于抽象化思想设计,拆分出「抽象工厂」与「具体工厂」,抽象工厂定义产品创建的标准接口,具体工厂子类负责创建对应类型的具体产品,将对象创建逻辑延迟到子类实现,完美符合开闭原则。
(2)、一工厂、一产品,抽象工厂 + 具体工厂分层,扩展仅需新增子类,符合开闭原则,是基础标准方案。
// 工厂方法模式标准实现(抽象解耦+灵活扩展) // 1. 抽象产品:约束所有产品的统一功能标准 public interface Product { void produce(); } // 2. 具体产品1:手机产品,实现抽象产品的业务逻辑 class Phone implements Product { @Override public void produce() { System.out.println("生产手机产品"); } } // 3. 具体产品2:电脑产品,实现抽象产品的业务逻辑 class Computer implements Product { @Override public void produce() { System.out.println("生产电脑产品"); } } //// 4. 方式一、抽象工厂:定义产品创建的标准接口,仅声明规则不负责创建 //public interface Factory { // Product createProduct(); //} // 4. 方式二、抽象工厂:定义产品创建的标准抽象类,仅声明规则不负责创建,添加泛型T由子类指定产品类型 public abstract class Factory<T extends Product> { public abstract T createProduct(); } // 5. 具体工厂1:手机工厂,专门创建手机产品,实现抽象工厂接口 // class PhoneFactory implements Factory {...} class PhoneFactory extends Factory<Product> { @Override public Product createProduct() { return new Phone(); } } // 6. 具体工厂2:电脑工厂,专门创建电脑产品,实现抽象工厂接口 // class ComputerFactory implements Factory {...} class ComputerFactory extends Factory<Product> { @Override public Product createProduct() { return new Computer(); } } // 工厂方法模式调用示例,按工厂获取产品,解耦且扩展灵活 class Client { public static void main(String[] args) { // 创建对应产品的专属工厂 Factory phoneFactory = new PhoneFactory(); Factory computerFactory = new ComputerFactory(); // 通过工厂获取产品,调用业务方法 Product phone = phoneFactory.createProduct(); Product computer = computerFactory.createProduct(); phone.produce(); computer.produce(); } }
类型三:抽象工厂模式
(1)、核心特点:针对产品簇设计(一组相关联、相互依赖的产品系列),抽象工厂中定义多个产品的创建接口,一个具体工厂可创建同一产品簇下的所有产品,解决多维度产品的统一创建问题,扩展性与规范性更强。
(2)、一工厂、多产品(产品簇),针对系列化产品设计,可批量创建关联产品,适配复杂的多维度产品场景。
// 抽象工厂模式标准实现(创建产品簇,适配多系列、多维度产品场景) // ========== 第一层:定义产品簇的所有抽象产品规范 ========== // 抽象产品1:手机产品规范 public interface Phone { void producePhone(); } // 抽象产品2:耳机产品规范(与手机属于同一产品簇) public interface Headset { void produceHeadset(); } // ========== 第二层:实现具体产品,划分不同产品系列 ========== // 小米系列-具体产品1:小米手机 class MiPhone implements Phone { @Override public void producePhone() { System.out.println("生产小米手机"); } } // 小米系列-具体产品2:小米耳机 class MiHeadset implements Headset { @Override public void produceHeadset() { System.out.println("生产小米耳机"); } } // 苹果系列-具体产品1:苹果手机 class IPhone implements Phone { @Override public void producePhone() { System.out.println("生产苹果手机"); } } // 苹果系列-具体产品2:苹果耳机 class IHeadset implements Headset { @Override public void produceHeadset() { System.out.println("生产苹果耳机"); } } // ========== 第三层:抽象工厂,定义产品簇的创建标准 ========== public interface ProductFactory { // 创建手机产品 Phone createPhone(); // 创建耳机产品 Headset createHeadset(); } // ========== 第四层:具体工厂,创建对应产品簇的所有产品 ========== // 小米工厂:创建小米产品簇(小米手机+小米耳机) class MiFactory implements ProductFactory { @Override public Phone createPhone() { return new MiPhone(); } @Override public Headset createHeadset() { return new MiHeadset(); } } // 苹果工厂:创建苹果产品簇(苹果手机+苹果耳机) class IFactory implements ProductFactory { @Override public Phone createPhone() { return new IPhone(); } @Override public Headset createHeadset() { return new IHeadset(); } } // 抽象工厂模式调用示例,一键获取整个产品簇,适配多系列场景 class Client { public static void main(String[] args) { // 创建小米产品簇工厂,批量获取小米系列产品 ProductFactory miFactory = new MiFactory(); miFactory.createPhone().producePhone(); miFactory.createHeadset().produceHeadset(); // 创建苹果产品簇工厂,批量获取苹果系列产品 ProductFactory iFactory = new IFactory(); iFactory.createPhone().producePhone(); iFactory.createHeadset().produceHeadset(); } }
六、行为型模式—策略模式:
1、核心思想:
将一组可替换的算法 / 业务规则封装为独立的 “策略类”,使算法的定义与使用算法的客户端代码完全解耦;客户端无需感知算法细节,可在运行时动态切换策略,新增策略只需扩展新类,无需修改原有代码,符合 “开闭原则”。
2、核心作用:
(1)、解耦策略逻辑与业务代码,避免大量 if-else 分支判断,让代码结构更清晰、可读性更强;
(2)、策略可动态切换,无需修改原有业务逻辑,灵活适配不同业务场景的规则变化;
(3)、符合开闭原则,新增策略时仅需扩展新的策略类,完全不侵入原有代码,降低维护风险;
(4)、统一管理所有策略,便于策略的复用、修改与单独维护。
3、策略模式标准实现:
策略模式的三层主要角色:
(1)、抽象策略类:通常由一个接口或抽象类实现,此角色定义所有策略的统一执行标准
(2)、具体策略类:实现了抽象策略定义的接口,提供具体业务逻辑
(3)、环境类(上下文类):封装策略的持有与调用,作为客户端与策略的中间层
// ========== 第一步:抽象策略类 ========== // 1. 抽象策略接口:定义所有策略的统一执行标准,约束策略核心方法 public interface PayStrategy { // 策略统一执行方法:支付 void pay(double amount); } // ========== 第二步:具体策略类 ========== // 2. 具体策略类1:支付宝支付策略,实现抽象策略接口 class AliPayStrategy implements PayStrategy { @Override public void pay(double amount) { System.out.println("使用支付宝支付,支付金额:" + amount + " 元"); } } // 3. 具体策略类2:微信支付策略,实现抽象策略接口 class WxPayStrategy implements PayStrategy { @Override public void pay(double amount) { System.out.println("使用微信支付,支付金额:" + amount + " 元"); } } // 4. 具体策略类3:银联支付策略,实现抽象策略接口(扩展新策略) class UnionPayStrategy implements PayStrategy { @Override public void pay(double amount) { System.out.println("使用银联支付,支付金额:" + amount + " 元"); } } // ========== 第三步:环境类(上下文类) ========== // 5. 上下文类:封装策略的持有与调用,作为客户端和策略的中间层,解耦二者 class PayContext { // 持有抽象策略对象,面向接口编程,兼容所有具体策略 private PayStrategy payStrategy; // 构造方法:动态注入指定策略 public PayContext(PayStrategy payStrategy) { this.payStrategy = payStrategy; } // 对外提供统一的支付调用入口,屏蔽策略内部执行细节 public void executePay(double amount) { payStrategy.pay(amount); } } // 策略模式调用示例,灵活选择/切换策略,无需关心策略内部逻辑 class Client { public static void main(String[] args) { // 场景1:选择支付宝支付策略 PayContext aliPay = new PayContext(new AliPayStrategy()); aliPay.executePay(99.0); // 场景2:切换为微信支付策略(直接注入新策略即可,无其他修改) PayContext wxPay = new PayContext(new WxPayStrategy()); wxPay.executePay(199.0); // 场景3:扩展银联支付策略(新增策略后,直接使用,原有代码无改动) PayContext unionPay = new PayContext(new UnionPayStrategy()); unionPay.executePay(299.0); } }
4、工厂模式与策略模式标准组合实现:
工厂模式 + 策略模式是日常开发最常用、最标准的组合实现方式,策略模式负责解耦业务逻辑、实现策略灵活切换,将不同业务规则封装为独立策略类,彻底消除代码中冗余的 if-else 分支;工厂模式负责统一创建所有策略对象,屏蔽策略对象的创建细节,让客户端无需关心「策略对象怎么来」,只需专注「策略怎么用」。二者结合实现双重解耦:既解耦了业务逻辑,又解耦了对象创建,代码规范性、扩展性、维护性拉满。
// ========== 第一步:抽象策略类 ========== // 抽象策略接口:约束所有支付策略的核心方法 public interface PayStrategy { // 支付统一执行方法 void pay(double amount); } // ========== 第二步:具体策略类 ========== // 具体策略1:支付宝支付策略 class AliPayStrategy implements PayStrategy { @Override public void pay(double amount) { System.out.println("支付宝支付成功,金额:" + amount + " 元"); } } // 具体策略2:微信支付策略 class WxPayStrategy implements PayStrategy { @Override public void pay(double amount) { System.out.println("微信支付成功,金额:" + amount + " 元"); } } // 具体策略3:银联支付策略 class UnionPayStrategy implements PayStrategy { @Override public void pay(double amount) { System.out.println("银联支付成功,金额:" + amount + " 元"); } } // ========== 第三步:工厂层 - 统一创建所有策略对象(核心组合点) ========== // 策略工厂类:简单工厂模式实现,包揽所有支付策略的创建逻辑,对外提供统一入口 class PayStrategyFactory { // 根据支付类型标识,创建对应策略对象,屏蔽创建细节 public static PayStrategy createStrategy(String payType) { if ("aliPay".equals(payType)) { return new AliPayStrategy(); } else if ("wxPay".equals(payType)) { return new WxPayStrategy(); } else if ("unionPay".equals(payType)) { return new UnionPayStrategy(); } throw new IllegalArgumentException("暂不支持该支付方式"); } } // ========== 第四步:环境类(上下文类) - 封装策略调用逻辑,解耦工厂与客户端 ========== class PayContext { // 持有抽象策略对象,面向接口编程 private PayStrategy payStrategy; // 传入支付类型,通过工厂获取对应策略 public PayContext(String payType) { this.payStrategy = PayStrategyFactory.createStrategy(payType); } // 对外提供统一支付入口,屏蔽策略调用细节 public void executePay(double amount) { payStrategy.pay(amount); } } // ========== 第五步:客户端调用示例 ========== class Client { public static void main(String[] args) { // 场景1:使用支付宝支付 → 仅传入支付类型标识即可 PayContext aliPayContext = new PayContext("aliPay"); aliPayContext.executePay(99.0); // 场景2:切换为微信支付 → 仅修改标识,无其他代码改动 PayContext wxPayContext = new PayContext("wxPay"); wxPayContext.executePay(199.0); // 场景3:使用银联支付 → 直接传新标识,扩展无侵入 PayContext unionPayContext = new PayContext("unionPay"); unionPayContext.executePay(299.0); } }
七、结构型模式—外观模式(门面模式):
1、核心思想:
为复杂的子系统提供一个统一的 “门面” 类作为对外交互入口,将子系统内部的复杂逻辑、多模块交互细节全部隐藏,客户端无需了解子系统的内部结构和调用规则,仅通过门面类即可完成对整个子系统的操作,大幅降低客户端与子系统之间的耦合度,简化客户端的使用成本。
2、核心作用:
(1)、极致简化客户端调用,将原本需调用多个子系统组件的多步操作,简化为调用外观类一个方法的一步操作,大幅降低调用成本;
(2)、解耦客户端与子系统,子系统内部逻辑(如新增组件、调整调用顺序)变更时,仅需修改外观类,客户端代码无需任何改动;
(3)、控制子系统访问范围,外观类可按需暴露必要接口,隐藏子系统的敏感方法,避免客户端误操作导致的异常;
(4)、降低客户端出错概率,避免客户端因直接操作多个子系统、记错调用顺序引发业务逻辑错误。
// ========== 第一步:定义子系统组件(模拟业务中的独立模块) ========== // 子系统1:投影仪(对应业务中的「订单模块」「库存模块」等) class Projector { public void on() { System.out.println("投影仪开启"); } public void off() { System.out.println("投影仪关闭"); } } // 子系统2:音响 class SoundSystem { public void on() { System.out.println("音响开启(音量调至80)"); } public void off() { System.out.println("音响关闭"); } } // 子系统3:播放器 class Player { public void play() { System.out.println("播放器开始播放电影"); } public void stop() { System.out.println("播放器停止播放"); } } // ========== 第二步:定义外观类(核心)- 封装所有子系统调用逻辑 ========== class HomeTheaterFacade { // 持有子系统实例,封装依赖关系 private Projector projector = new Projector(); private SoundSystem soundSystem = new SoundSystem(); private Player player = new Player(); // 对外暴露的简化接口:一键开启影院 public void startMovie() { projector.on(); soundSystem.on(); player.play(); System.out.println("===== 家庭影院就绪,开始观影 ====="); } // 对外暴露的简化接口:一键关闭影院 public void endMovie() { player.stop(); soundSystem.off(); projector.off(); System.out.println("===== 家庭影院已关闭 ====="); } } // ========== 第三步:客户端调用示例 - 仅操作外观类,无需关注子系统细节 ========== class Client { public static void main(String[] args) { // 客户端仅创建外观类实例,无需创建任何子系统对象 HomeTheaterFacade theater = new HomeTheaterFacade(); // 一键开启影院(无需关心子系统调用顺序) theater.startMovie(); System.out.println("\n----- 观影结束 ----- \n"); // 一键关闭影院(无需操作任何子系统方法) theater.endMovie(); } }
八、行为型模式—责任链模式:
1、核心思想:
将多个请求处理者按顺序连成一条链式结构,当请求产生时,会沿着这条链依次传递;每个处理者只需判断自身是否能处理该请求:能处理则直接处理,不能则将请求传递给链上的下一个处理者。这种模式彻底解耦了请求的发送者与多个处理者之间的依赖关系,支持动态增减、调整处理者顺序,提升系统的灵活性。
2、核心作用:
(1)、解耦请求双方:发起者无需知道处理者的数量、层级和处理逻辑,处理者也无需知道请求来源,仅关注自身职责;
(2)、灵活组装链结构:基于单链表特性,可动态增删、调整处理节点顺序,符合 “开闭原则”;
(3)、简化节点职责:每个处理节点仅需实现自身的处理逻辑,无需关注链的整体传递规则;
(4)、避免请求堆积:请求沿链有明确传递路径,可通过链尾节点统一处理 “无人能处理” 的请求。
3、责任链模式标准实现:
责任链模式的三层主要角色:
(1)、抽象处理者角色:定义责任链的核心骨架,包含单链表的 “后继节点引用”(存储下一个处理者)、节点拼接方法、抽象处理方法(由子类实现具体逻辑);
(2)、具体处理者角色:实现抽象处理方法,完成自身的业务处理逻辑,决定是否将请求传递给后继节点;
(3)、客户端角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
// 抽象处理者:责任链核心骨架(链表结构) abstract class Approver { // 核心:责任链基于单链表设计,此处存储「后继节点」,是请求传递的核心载体 protected Approver nextApprover; // 辅助属性:标识节点名称,便于追踪请求传递路径 protected String name; // 构造方法:初始化节点名称 public Approver(String name) { this.name = name; } // 核心:拼接单链表节点,完成责任链组装 // 作用:将当前节点的后继节点指向传入的节点,实现多节点串联 public void setNext(Approver next) { this.nextApprover = next; } // 抽象处理方法:每个节点的核心处理入口,由子类实现具体逻辑 public abstract void approveLeave(int days); } // 具体处理者1:班长(仅处理≤1天的请假) class Monitor extends Approver { public Monitor() { super("班长"); } @Override public void approveLeave(int days) { // 自身处理逻辑 if (days <= 1) { System.out.println(name + "审批通过:请假" + days + "天"); } else { // 处理不了则传递给后继节点 System.out.println(name + "无权限审批" + days + "天假,传递给:" + nextApprover.name); nextApprover.approveLeave(days); } } } // 具体处理者2:班主任(仅处理≤3天的请假) class Teacher extends Approver { public Teacher() { super("班主任"); } @Override public void approveLeave(int days) { // 自身处理逻辑 if (days <= 3) { System.out.println(name + "审批通过:请假" + days + "天"); } else { // 链尾无后继节点,请求终止 System.out.println(name + "无权限审批" + days + "天假,已到链尾,审批失败"); } } } // 客户端:手动组装责任链并发起请求 public class SimpleChainDemo { public static void main(String[] args) { // 1. 创建单链表节点 Approver monitor = new Monitor(); Approver teacher = new Teacher(); // 2. 拼接责任链:班长 → 班主任 monitor.setNext(teacher); // 3. 发起请求(从链头触发) System.out.println("=== 请求1:请假1天 ==="); monitor.approveLeave(1); System.out.println("\n=== 请求2:请假2天 ==="); monitor.approveLeave(2); } }
4、工厂模式与责任链模式标准组合实现:
将责任链的组装逻辑抽离到工厂类中,客户端无需关心 “节点如何拼接”“谁是前驱/后继”,仅需调用工厂方法即可获取组装好的责任链头节点;后续若需调整审批流程(如新增节点、修改顺序),仅需修改工厂类,符合 “开闭原则”。
// 1. 抽象处理者+具体处理者 // 抽象处理者:责任链核心骨架(单链表结构) abstract class Approver { // 核心:责任链基于单链表设计,此处存储「后继节点」,是请求传递的核心载体 protected Approver nextApprover; // 辅助属性:标识节点名称,便于追踪请求传递路径 protected String name; // 构造方法:初始化节点名称 public Approver(String name) { this.name = name; } // 核心:拼接单链表节点,完成责任链组装 // 作用:将当前节点的后继节点指向传入的节点,实现多节点串联 public void setNext(Approver next) { this.nextApprover = next; } // 抽象处理方法:每个节点的核心处理入口,由子类实现具体逻辑 public abstract void approveLeave(int days); } // 具体处理者1:班长(仅处理≤1天的请假) class Monitor extends Approver { public Monitor() { super("班长"); } @Override public void approveLeave(int days) { // 自身处理逻辑 if (days <= 1) { System.out.println(name + "审批通过:请假" + days + "天"); } else { // 处理不了则传递给后继节点 System.out.println(name + "无权限审批" + days + "天假,传递给:" + nextApprover.name); nextApprover.approveLeave(days); } } } // 具体处理者2:班主任(仅处理≤3天的请假) class Teacher extends Approver { public Teacher() { super("班主任"); } @Override public void approveLeave(int days) { // 自身处理逻辑 if (days <= 3) { System.out.println(name + "审批通过:请假" + days + "天"); } else { // 处理不了则传递给后继节点 System.out.println(name + "无权限审批" + days + "天假,传递给:" + nextApprover.name); nextApprover.approveLeave(days); } } } // 新增扩展节点:年级主任 class Director extends Approver { public Director() { super("年级主任"); } @Override public void approveLeave(int days) { if (days <= 7) { System.out.println(name + "审批通过:请假" + days + "天"); } else { System.out.println(name + "无权限审批" + days + "天假,已到链尾,审批失败"); } } } // 2. 责任链工厂类:封装组装逻辑 class ApproverChainFactory { /** * 工厂方法:创建固定的请假审批责任链 * 对外隐藏组装细节,仅暴露“获取链头”的接口 */ public static Approver createLeaveApproverChain() { // 1. 创建所有处理节点 Approver monitor = new Monitor(); Approver teacher = new Teacher(); Approver director = new Director(); // 2. 内部拼接责任链:班长 → 班主任 → 年级主任 monitor.setNext(teacher); teacher.setNext(director); // 3. 返回链头节点(客户端仅需操作链头) return monitor; } } // 3. 客户端:调用工厂获取责任链,无需关心组装细节 public class FactoryChainDemo { public static void main(String[] args) { // 直接从工厂获取组装好的责任链 Approver chainHead = ApproverChainFactory.createLeaveApproverChain(); // 发起请求(逻辑与手动组装完全一致) System.out.println("=== 请求1:请假5天 ==="); chainHead.approveLeave(5); System.out.println("\n=== 请求2:请假8天 ==="); chainHead.approveLeave(8); } }
九、结构型模式—代理模式:
1、核心思想:
为目标对象(真实业务对象)提供一个代理对象,代理与目标对象实现相同接口/继承同一父类,对外暴露统一访问入口;
通过代理对象在不修改目标对象代码的前提下,实现功能增强和访问管控。
根据代理类的创建时机,分为 静态代理(编译期生成)和 动态代理(运行期生成)。
|
类型 |
说明 |
|
静态代理 |
代理类在编译期提前编写完成,一个代理类仅服务于一个特定的目标类,实现简单、易调试,但扩展性差(新增目标类需同步新增代理类); |
|
JDK 动态代理 |
基于接口实现,运行时通过反射动态生成代理类,无需手动编写代理类,一个动态代理可服务于多个实现同一接口的目标类,但要求目标类必须实现接口; |
|
CGLIB 动态代理 |
基于继承实现,运行时动态生成目标类的子类作为代理类,无需目标类实现接口,适配无接口的场景,但无法代理final类 / 方法(子类无法继承); |
2、核心作用:
(1)、功能增强:统一为目标方法添加日志、事务、权限校验、耗时统计等非核心逻辑,符合开闭原则。
(2)、逻辑解耦:将通用非核心逻辑从目标对象抽离,让目标对象聚焦核心业务,降低代码耦合度。
(3)、访问管控:灵活限制目标方法的调用权限、频次(如接口限流)、时机(如懒加载)。
(4)、框架底层支撑:是 Spring AOP、@Transactional声明式事务的核心实现原理。
3、静态代理:
代理类由程序员手动编写,编译期就已确定;代理类与目标类实现相同接口,持有目标类的引用。
|
优点 |
缺点 |
|
实现简单、无框架依赖、逻辑清晰 |
代码冗余:一个目标类对应一个代理类 扩展性差:新增方法需同步修改代理类 |
注:适用于少量固定、简单的代理场景,如工具类的简单功能增强。
(1)、核心角色:
|
核心角色 |
核心作用 |
|
抽象主题(接口) |
定义目标类和代理类的统一业务方法,是两者的 “通用访问标准” |
|
真实主题(目标类) |
实现抽象主题接口,仅编写核心业务逻辑,是代理类的 “增强目标” |
|
代理类 |
实现抽象主题接口 + 持有真实主题引用,封装增强逻辑并调用目标类方法 |
(2)、执行流程:
静态代理的执行流程主要分两部分:
- 第一部分是前置准备:定义统一业务接口 → 目标类实现这个接口,仅编写核心业务逻辑 → 代理类实现同一接口,同时持有目标类的引用;
- 第二部分是核心调用:客户端调用代理类的方法 → 代理类先执行前置增强逻辑(比如日志、权限校验)→ 代理类直接调用目标类的真实业务方法 → 代理类再执行后置增强逻辑(比如耗时统计)→ 最后把结果返回给客户端。
// 静态代理示例 public class StaticProxyDemo { // ===================== 流程1:定义统一业务接口 ===================== interface BusinessService { // 核心业务方法 void doBusiness(String param); } // ===================== 流程2:目标类实现该接口(核心业务) ===================== static class BusinessServiceImpl implements BusinessService { @Override public void doBusiness(String param) { // 仅编写核心业务逻辑 System.out.println("【目标类】执行核心业务,参数:" + param); } } // ===================== 流程3:代理类实现同一接口并持有目标类引用 ===================== static class BusinessServiceProxy implements BusinessService { // 持有目标类引用 private final BusinessService target; // 构造器注入目标对象 public BusinessServiceProxy(BusinessService target) { this.target = target; } @Override public void doBusiness(String param) { // ===================== 流程5:代理类执行前置增强逻辑 ===================== System.out.println("【代理类】前置增强:日志记录、权限校验"); long startTime = System.currentTimeMillis(); // ===================== 流程6:代理类直接调用目标类真实方法 ===================== target.doBusiness(param); // ===================== 流程7:代理类执行后置增强逻辑 ===================== long endTime = System.currentTimeMillis(); System.out.println("【代理类】后置增强:耗时统计(" + (endTime - startTime) + "ms)"); // ===================== 流程8:返回结果至客户端(void无显式return) ===================== } } // ===================== 客户端测试(流程4开始) ===================== public static void main(String[] args) { // 初始化目标类 BusinessService target = new BusinessServiceImpl(); // 初始化代理类(持有目标类引用) BusinessService proxy = new BusinessServiceProxy(target); // ===================== 流程4:客户端调用代理类方法 ===================== proxy.doBusiness("测试参数"); } }
4、动态代理:
代理类无需程序员手动编写,而是在程序运行期由 JVM 通过反射(JDK 代理)或字节码生成技术(CGLIB 代理)动态创建;
代理类可适配多个目标类,在不修改目标类代码的前提下,统一为目标方法添加增强逻辑,解决静态代理代码冗余的问题。
依据代理类的底层实现技术,可分为 JDK 动态代理(基于反射)、CGLIB 动态代理(基于字节码)。
|
优点 |
缺点 |
|
1、无代码冗余,一个代理逻辑适配多目标类 2、扩展性强,新增方法无需改代理逻辑 3、支撑 Spring AOP、声明式事务等框架核心 |
1、实现稍复杂,依赖反射 / 字节码技术 2、JDK 代理需目标类实现接口,CGLIB 代理不支持 final 类 / 方法 |
注:
反射机制:反射是 Java 提供的核心能力,指程序在运行期可获取任意类的完整结构信息(包括类名、方法、字段等),并能通过这些信息调用类的任意方法、访问 / 修改字段,无需在编译期提前确定目标类,也是 JDK 动态代理实现的核心基础。
(1)、JDK 动态代理:
JDK 代理依赖 JDK 原生反射实现:要求目标类必须实现至少一个接口(因 JDK 代理基于接口生成代理类);核心通过 Proxy 类生成代理对象,借助 InvocationHandler 接口的 invoke () 方法封装前置 / 后置增强逻辑,最终通过反射调用目标类的真实方法。
执行流程:
定义业务接口和目标类(实现接口)→ 自定义 InvocationHandler 并重写invoke()封装增强逻辑 → 通过Proxy.newProxyInstance()生成代理对象 → 客户端调用代理方法 → 触发invoke() → 执行增强逻辑 + 反射调用目标方法;
注:
- Proxy类:JDK 代理的核心工具类,通过Proxy.newProxyInstance()方法在运行期动态生成代理类对象,入参需指定目标类的类加载器、实现的接口、自定义的 InvocationHandler;
- InvocationHandler接口:增强逻辑的核心载体,需自定义实现类并重写invoke()方法——所有代理对象的方法调用都会触发该方法,方法内可封装前置 / 后置增强逻辑,再通过Method.invoke()反射调用目标类的真实方法;
// JDK动态代理示例 public class JdkProxyDemo { // 1. 抽象主题(接口) interface OrderService { void createOrder(String orderNo); } // 2. 真实主题(目标类) static class OrderServiceImpl implements OrderService { @Override public void createOrder(String orderNo) { System.out.println("核心业务:创建订单 " + orderNo); } } // 3. 自定义InvocationHandler(增强逻辑) static class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置增强 System.out.println("【前置】日志:调用" + method.getName() + "方法"); // 反射调用目标方法 Object result = method.invoke(target, args); // 后置增强 System.out.println("【后置】耗时统计:方法执行完成"); return result; } } // 4. 主方法(核心调用) public static void main(String[] args) { // 创建目标对象 OrderService target = new OrderServiceImpl(); // 动态生成代理对象 OrderService proxy = (OrderService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new MyInvocationHandler(target) ); // 调用代理方法 proxy.createOrder("ORDER_CESHI"); } }
(2)、CGLIB 动态代理:
CGLIB 代理依赖 ASM 字节码框架实现:无需目标类实现接口,但因基于继承生成代理子类,故目标类不能是 final 类、方法不能是 final(final 类无法继承,final 方法无法重写);核心通过 Enhancer 类生成目标类的子类作为代理对象,借助 MethodInterceptor 接口的 intercept () 方法封装前置 / 后置增强逻辑。
执行流程:
编写无接口的目标类(核心业务)→ 自定义 MethodInterceptor 并重写intercept()封装增强逻辑 → 通过Enhancer设置父类 + 绑定拦截器,生成代理子类 → 客户端调用代理方法 → 触发intercept() → 执行增强逻辑 + 调用目标方法;
- Enhancer类:CGLIB 代理的核心工具类,通过设置目标类为父类、绑定 MethodInterceptor,调用create()方法在运行期动态生成目标类的子类(代理类);
- MethodInterceptor接口:增强逻辑的核心载体,需自定义实现类并重写intercept()方法 ——所有代理对象的方法调用都会触发该方法,方法内可封装前置 / 后置增强逻辑,再通过MethodProxy.invokeSuper()调用目标类的真实方法;
<!-- CGLIB 依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; // CGLIB动态代理示例 public class CglibProxyDemo { // 1. 真实主题(目标类,无需接口) static class UserService { public void updateUser(String userId) { System.out.println("核心业务:更新用户 " + userId); } } // 2. 自定义MethodInterceptor(增强逻辑) static class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 前置增强 System.out.println("【前置】权限校验:调用" + method.getName() + "方法"); // 调用目标类方法 Object result = proxy.invokeSuper(obj, args); // 后置增强 System.out.println("【后置】日志:方法执行完成"); return result; } } // 3. 主方法(核心调用) public static void main(String[] args) { // 创建增强器 Enhancer enhancer = new Enhancer(); // 设置目标类为父类 enhancer.setSuperclass(UserService.class); // 绑定增强逻辑 enhancer.setCallback(new MyMethodInterceptor()); // 生成代理对象 UserService proxy = (UserService) enhancer.create(); // 调用代理方法 proxy.updateUser("USER_CESHI"); } }
(3)、JDK 动态代理和 CGLIB 动态代理的核心区别:
|
实现基础不同: |
1、JDK 代理基于接口,要求目标类必须实现接口; 2、CGLIB 代理基于继承,目标类不用实现接口,但不能是 final 类或有 final 方法; |
|
底层技术不同: |
1、JDK 代理用的是 JDK 原生反射; 2、CGLIB 依赖 ASM 字节码框架生成目标类的子类; |
|
性能和局限不同: |
1、JDK8 之后性能比 CGLIB 好,但只能代理有接口的类; 2、CGLIB 能代理任意类,却处理不了 final 相关的类 / 方法; |
|
注:Spring 里默认优先用 JDK 动态代理,若目标类未实现任何接口,自动切换为 CGLIB 动态代理;也可通过配置强制使用 CGLIB。 |
|
浙公网安备 33010602011771号