【Spring】设计模式(GOF)
Spring Framework在其架构和实现中广泛使用了多种GOF(Gang of Four)设计模式。这些设计模式帮助Spring解决了许多常见的软件开发问题,提高了代码的可重用性、可维护性和可扩展性。
1、工厂模式(Factory Pattern)
1.1简介
- Spring使用工厂模式来管理bean的创建和实例化。
- BeanFactory和ApplicationContext接口就是工厂模式的典型应用,它们负责根据配置信息或注解来创建和管理bean实例。
- FactoryBean接口则是基于抽象工厂模式设计的,它允许用户自定义bean的创建逻辑。
- 工厂模式有助于降低代码间的耦合度,并提高代码的可重用性和可扩展性。
1.2主要涉及角色
- 抽象工厂(Abstract Factory):定义一个创建对象的接口,但不负责具体的对象创建过程。它通常是一个接口或者抽象类,其中定义了一个或多个创建对象的方法。
- 具体工厂(Concrete Factory):实现抽象工厂接口,负责实际创建具体的对象。每个具体工厂都对应着一种具体的对象类型。
- 产品(Product):工厂所创建的对象类型。它可以是一个接口、抽象类或者具体类。
- 具体产品(Concrete Product):实现产品接口的具体对象。
1.3代码示例
步骤一:
定义一个产品接口(例如Animal)
public interface Animal { void makeSound();
}
步骤二:
创建实现该接口的具体类(例如Dog和Cat)
public class Dog implements Animal { @Override public void makeSound() { System.out.println("Woof woof!"); }
} public class Cat implements Animal { @Override public void makeSound() { System.out.println("Meow meow!"); }
}
步骤三:
创建一个简单工厂类(例如AnimalFactory),该类负责创建Animal对象
public class AnimalFactory { // 静态方法,根据传入的类型参数返回相应的Animal对象 public static Animal createAnimal(String type) { if ("dog".equalsIgnoreCase(type)) { return new Dog(); } else if ("cat".equalsIgnoreCase(type)) { return new Cat(); } else { return null; // 或者抛出一个异常 } }
}
步骤四:
在客户端代码中使用这个工厂类来创建对象
public class Main { public static void main(String[] args) { // 使用工厂类创建Dog对象 Animal dog = AnimalFactory.createAnimal("dog"); dog.makeSound(); // 输出: Woof woof! // 使用工厂类创建Cat对象 Animal cat = AnimalFactory.createAnimal("cat"); cat.makeSound(); // 输出: Meow meow! // 尝试创建一个不存在的动物类型,将返回null或抛出异常(取决于工厂类的实现) Animal unknownAnimal = AnimalFactory.createAnimal("unknown"); if (unknownAnimal != null) { unknownAnimal.makeSound(); } else { System.out.println("Unknown animal type!"); } }
}
2、单例模式(Singleton Pattern)
2.1简介
- 在Spring的默认作用域中,每个bean都是单例的,这意味着在整个应用程序的生命周期中,一个bean的ID只对应一个实例对象。
2.2应用场景
- Windows的任务管理器、回收站等,只能有一个实例。
- 计数器、配置管理、日志记录等需要全局唯一管理的类。
- 数据库连接池、线程池等需要频繁创建和销毁的实例,使用单例模式可以节省开销。
2.3实现原理
单例模式的核心思想是将类的实例化过程控制在一个类中,避免其他类实例化该类对象。它通常通过以下步骤实现:
- 私有化构造函数:将类的构造函数私有化,防止外部类通过new关键字创建实例。
- 创建静态实例:在类中定义一个静态的私有实例,用于存储该类的唯一实例。
- 提供全局访问点:通过提供一个静态的公共方法(通常是getInstance()方法),允许外部类访问该类的唯一实例。如果静态实例尚未创建,则在该方法中创建它。
2.4实现方式
在Java中,单例模式有多种实现方式,其中常见的有:
1、饿汉式(线程安全):
- 在类加载时就已经完成了实例化,所以类加载较慢,但获取对象的速度快。
- 代码示例如下
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; }
}
2、懒汉式(线程不安全):
- 在第一次调用getInstance()方法时才进行实例化,但这种方法在多线程环境下可能会产生多个实例。
- 代码示例如下
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
}
3、静态内部类实现:
- 利用JVM的类加载机制来保证初始化instance时只有一个线程,既线程安全又延迟加载。
- 代码示例如下
public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; }
}
4、枚举实现:
- 利用枚举的特性,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
- 代码示例如下
public enum Singleton { INSTANCE; // 其他方法...
}
3、观察者模式(Observer Pattern)
3.1简介
- Spring的事件驱动模型就是基于观察者模式实现的。
- 当某个事件发生时,Spring会通知所有注册的观察者(即事件监听器)来执行相应的操作。
- 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
3.2实现原理
- 注册(Attach):观察者将自己注册到主题中,以便在主题状态发生改变时收到通知。这通常通过调用主题的attach()或类似的方法来完成。
- 通知(Notify):当主题的状态发生改变时,它会遍历其观察者列表,并调用每个观察者的update()方法(或类似的方法)来通知它们。在调用update()方法时,主题通常会传递一些有关状态改变的信息给观察者。
- 更新(Update):观察者收到通知后,会根据主题传递的信息来更新自己的状态。这通常涉及重新计算值、更改用户界面或其他操作。
4、适配器模式(Adapter Pattern)
4.1简介
- Spring MVC中的Controller适配器就是适配器模式的一个例子。
- 它允许开发者使用不同类型的Controller来处理请求,而无需修改Spring MVC的核心代码。
- 有目标接口(Target):客户端所期望的接口、适配者(Adaptee):需要被适配的类或者接口、适配器(Adapter):将适配者的接口转换成目标接口的类这三个主要角色
4.1实现方式
- 类适配器:适配器通过多重继承的方式实现目标接口,并继承适配者类。适配器中通常需要实现目标接口中定义的方法,并在这些方法中调用适配者的相关方法。
- 对象适配器:适配器通过持有一个适配者对象的引用来实现目标接口。适配器中同样需要实现目标接口中定义的方法,并在这些方法中调用适配者对象的相关方法。对象适配器通过组合的方式实现了适配,相对于类适配器更加灵活。
5、策略模式(Strategy Pattern)
5.1简介
- Spring的事务管理机制就使用了策略模式。
- 通过定义不同的事务管理策略(如编程式事务管理、声明式事务管理等),应用可以灵活地选择不同的事务处理方式。
5.2主要角色
- 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(ConcreteStrategy)角色:实现了抽象策略角色所定义的接口,封装了具体的算法或行为。
- 环境(Context)角色:持有一个策略对象的引用,并最终给客户端调用。
5.3实现步骤
- 定义策略接口(Strategy):定义一个接口或抽象类,用于封装算法。
- 实现具体策略类(ConcreteStrategy):创建实现了策略接口的实体类。
- 创建环境类(Context):该类中持有一个策略对象的引用,最终给客户端调用。
6、模板方法模式(Template Method Pattern)
6.1简介
- Spring中的JdbcTemplate、HibernateTemplate等就是模板方法模式的实现。
- 它们定义了一个算法的骨架,而将一些步骤延迟到子类中实现。
- 模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
6.2主要角色
- 抽象类(Abstract Class):定义了一个或多个抽象操作和模板方法。这些抽象操作在抽象类中可能没有具体的实现,需要子类来实现。模板方法是一个具体的方法,它调用了一个或多个抽象操作。
- 具体子类(Concrete Subclass):实现了抽象类中的抽象操作,这些操作在子类中可以有具体的实现。子类通过继承抽象类,并覆写其中的抽象操作来实现算法的特定步骤。
6.3实现步骤
- 定义抽象类:在抽象类中定义算法的框架,包括一个或多个抽象操作和模板方法。模板方法按照算法的顺序调用抽象操作。
- 实现具体子类:创建具体子类,继承抽象类,并覆写其中的抽象操作。这些覆写的方法将实现算法的特定步骤。
- 使用模板方法:在客户端代码中,创建具体子类的实例,并调用模板方法。模板方法会按照定义的顺序调用抽象操作,而抽象操作的具体实现在子类中定义。因此,算法的执行会根据子类的实现而有所不同。
7、代理模式(Proxy Pattern)
7.1简介
- Spring AOP(面向切面编程)和Spring Security底层都使用了代理模式。
- 通过代理对象来增强目标对象的功能,实现横切关注点(如事务、日志、安全等)的织入。
7.2主要角色
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
7.3分类
- 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。静态代理在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
- 动态代理:在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
8、装饰器模式(Decorator Pattern)
8.1简介
- 装饰器模式允许向一个现有的对象添加新的功能,同时保持结构的开放性。
- 在Spring中,这可以体现在通过配置文件或注解来动态地给bean添加新的行为或属性。
8.2主要角色
- 抽象组件接口(Component):定义了一个接口,用于规范具体组件和装饰器对象的行为。
- 具体组件类(ConcreteComponent):实现了抽象组件接口,是系统需要动态添加功能的对象。
- 抽象装饰器类(Decorator):实现了抽象组件接口,并持有一个指向抽象组件对象的引用。装饰器类可以在其方法中调用被装饰对象的相应方法,并在其前后添加额外的功能。
- 具体装饰器类(ConcreteDecorator):继承自抽象装饰器类,通过扩展抽象装饰器类的方法来实现具体的装饰功能。
8.3实现流程
- 定义组件接口,包含原始对象和装饰器对象的共同操作。
- 创建具体组件类,实现组件接口的方法,提供基本功能。
- 创建装饰器类,继承或实现组件接口,并持有一个组件对象的引用。
- 在装饰器类中,调用组件对象的方法,并在其前后添加额外的功能。
- 创建具体装饰器类,扩展装饰器类,并提供具体的功能扩展。
- 在客户端中,使用装饰器模式,通过组件接口操作装饰后的对象。
9、组合模式(Composite Pattern)
9.1简介
- Spring本身并没有直接实现组合模式,但它的许多组件(如Bean容器、应用上下文等)都体现了组合模式的思想。这些组件可以包含其他组件,形成一个树形结构,从而支持递归组合和统一操作。
9.2主要角色
- 抽象构件角色(Component):这是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。这个接口可以用来访问和管理所有的子对象。
- 树枝构件角色(Composite):这是组合中的分支节点对象,表示具有子节点的对象。它实现了Component接口,并包含对子节点的引用。树枝构件可以包含其他树枝构件或叶子构件。
- 树叶构件角色(Leaf):在组合树中表示叶节点对象,叶节点没有子节点。它实现了Component接口,但不需要包含对子节点的引用。
- 客户角色(Client):通过Component接口操纵组合部件的对象。
9.3实现方式
- 定义抽象接口:首先定义一个包含公共方法的抽象接口(Component)。这个方法应该用于访问和管理Component子部件。
- 实现树枝构件和树叶构件:接着,创建实现Component接口的树枝构件(Composite)和树叶构件(Leaf)。树枝构件应该包含对子节点的引用,并实现与子节点相关的操作,如添加和删除子节点。树叶构件则不包含对子节点的引用。
- 创建客户角色:最后,创建客户角色(Client),该角色通过Component接口与组合对象进行交互。客户角色可以像处理单个对象一样处理组合对象,而无需关心其内部结构。
相关文章:
【Spring】设计模式(GOF)
Spring Framework在其架构和实现中广泛使用了多种GOF(Gang of Four)设计模式。这些设计模式帮助Spring解决了许多常见的软件开发问题,提高了代码的可重用性、可维护性和可扩展性。 1、工厂模式(Factory Pattern) 1.1简…...
php抖音详情和关键词搜索api
抖音详情和关键词搜索的 API 可以通过抖音提供的开放平台来获取。以下是使用 PHP 实现的示例代码: 获取抖音视频详情 API: 获取Key和secret请移步 <?php$accessToken YOUR_ACCESS_TOKEN; // 替换为自己的 access_token $itemId YOUR_ITEM_ID; /…...
SOCKS 代理 和 HTTP 代理
SOCKS 代理 和 HTTP 代理 的区别 SOCKS 代理 和 HTTP 代理 都是代理服务器,它们充当客户端和目标服务器之间的中介,但它们的工作方式和应用场景有所不同。 1. SOCKS 代理: 工作原理: SOCKS 代理是一种更底层的代理,…...

【Linux】自己实现一个bash进程
bash就是命令行解释器,就是Linux操作系统让我们看到的,与用户进行交互的一种外壳(shell),当然了bash也是一个进程,它有时候就是通过创建子进程来执行我们输入的命令的。这无疑就离不开我们上篇博客所说的进…...

记录深度学习GPU配置,下载CUDA与cuDnn
目标下载: cuda 11.0.1_451.22 win10.exe cudnn-11.0-windows-x64-v8.0.2.39.zip cuda历史版本网址 CUDA Toolkit Archive | NVIDIA Developer 自己下载过11.0.1版本 点击下载local版本,本地安装,有2个多GB,很大,我不喜欢network版本,容易掉线 cuDnn https://developer.nvi…...

Word将表格调成合适的大小
请等待内容完善...
2024HBCPC:C Goose Goose Duck
题目描述 Iris 有 n n n 个喜欢玩鹅鸭杀的朋友,编号为 1 ∼ n 1∼n 1∼n。 假期的时候,大家经常会在群里问有没有人玩鹅鸭杀,并且报出现在已经参与的人数。 但是每个人对于当前是否加入游戏都有自己的想法。 具体的来说,对于第…...

Llama 3 模型家族构建安全可信赖企业级AI应用之使用 Llama Guard 保护大模型对话 (八)
LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (一) 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (二) 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (三) 基于 LlaMA…...
《一地霜白》读书笔记
1.3.6 街灯明灭,勾缀成行,为了生者与死者 “很多年过去了。回头看,沿着一排暗中的街灯,两三盏灭了,郁闷中有意外的欣喜:街灯明灭,勾缀成行,为了生者与死者。” 童年、青少年在人的…...
在Java中实现多线程之间的通信
一、技术难点 在Java中实现多线程之间的通信是一个复杂但重要的任务,它涉及到线程同步、数据共享和线程间协作等多个方面。以下是实现多线程通信时可能遇到的一些技术难点: 线程同步:多线程环境下,多个线程可能同时访问和修改共享…...
Python中的json.dump与json.dumps对比
Python中的json.dump与json.dumps对比 json.dumps()json.dump() json.dumps() dumps 是 “dump string” 的缩写。它将Python对象转换(序列化)为JSON格式的字符串。数据被转换为一个字符串,并且这个字符串可以直接被写入文件、发送到网络&am…...

【从零开始学习RabbitMQ | 第二篇】如何确保MQ的可靠性和消费者可靠性
目录 前言: MQ可靠性: 数据持久化: Lazy Queue: 消费者可靠性: 消费者确认机制: 消费失败处理: MQ保证幂等性: 方法一: 总结: 前言: …...

常用批处理命令及批处理文件编写技巧
一常用批处理命令 1.查看命令用法:命令 /? //如:cd /? 2.切换盘符目录:cd /d D:\test 或直接输入 d: //进入上次d盘所在的目录 3.切换目录:cd test 4.清屏:cls 5.“arp -a” //它会列出当前设备缓存中的所有…...
android NetworkMonitor记录
是否能上网的状态 上网url地址的设置: NetworkMonitor.java makeCaptivePortalHttpsUrls config_captive_portal_https_urls DEFAULT_CAPTIVE_PORTAL_HTTPS_URLS http准备监测 isCaptivePortal sendHttpAndHttpsParallelWithFallbackProbes httpsProbe.start();…...

OSPF优化——OSPF减少LSA更新量2
二、特殊区域——优化非骨干区域的LSA数量 不是骨干区域、不能存在虚链路 1、不能存在 ASBR 1)末梢区域 该区域将拒绝 4、5LSA的进人,同时由该区域连接骨干0区域的ABR 向该区域,发布一条3类的缺省路由; 该区域内每台路由器均需配置…...
【AMS】Android 8.0+ 绕开启动后台Service限制
一、背景 应客户要求,需要在开机时,拉起应用A。但因为开机时,同时被拉起的应用过多,导致Launcher在开机那一刻较为卡顿。为解决这一问题,采取了延迟拉起的做法。在开机后,延迟一定时间,由系统服务,拉起应用A。 于是乎,就出现这么个报错: Not allowed to start ser…...

【多态】(超级详细!)
【多态】(超级详细!) 前言一、 多态的概念二、重写1. 方法重写的规则2. 重写和重载的区别 三、多态实现的条件四、 向上转型五、动态绑定 前言 面向对象的三大特征:封装性、继承性、多态性。 extends继承或者implements实现&…...

vue的组件化
vue的组件化 vue的组件化,就是根据功能、业务逻辑、数据流向等因素进行划分把页面拆分成多个组件。组件是资源独立的,组件也可以相互嵌套。目的是提高代码的可读性、可维护性和可复用性。 组件化思想体现 组件封装步骤 1.公共组件 公共组件全局注…...

spark的简单学习一
一 RDD 1.1 RDD的概述 1.RDD(Resilient Distributed Dataset,弹性分布式数据集)是Apache Spark中的一个核心概念。它是Spark中用于表示不可变、可分区、里面的元素可并行计算的集合。RDD提供了一种高度受限的共享内存模型,即RD…...

【第5章】SpringBoot整合Druid
文章目录 前言一、启动器二、配置1.JDBC 配置2.连接池配置3. 监控配置 三、配置多数据源1. 添加配置2. 创建数据源 四、配置 Filter1. 配置Filter2. 可配置的Filter 五、获取 Druid 的监控数据六、案例1. 问题2. 引入库3. 配置4. 配置类5. 测试类6. 测试结果 七、案例 ( 推荐 )…...

力扣654. 最大二叉树
Problem: 654. 最大二叉树 文章目录 题目描述思路复杂度Code 题目描述 思路 对于构造二叉树这类问题一般都是利用先、中、后序遍历,再将原始问题分解得出结果 1.定义递归函数build,每次将一个数组中的最大值作为当前子树的根节点构造二叉树;…...
基于Netty实现WebSocket客户端
本文是基于Netty快速上手WebSocket客户端,不涉及WebSocket的TLS/SSL加密传输。 WebSocket原理参考【WebSocket简介-CSDN博客】,测试用的WebSocket服务端也是用Netty实现的,参考【基于Netty实现WebSocket服务端-CSDN博客】 一、基于Netty快速…...

homebrew安装mysql的一些问题
本文目录 一、Homebrew镜像安装二、mac安装mysql2.1、修改mysql密码 本文基于mac环境下进行的安装 一、Homebrew镜像安装 Homebrew国内如何自动安装,运行命令/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 会…...
产线问题排查
CPU过高 使用top命令查看占用CPU过高的进程。 导出CPU占用高进程的线程栈。 jstack pid >> java.txt Java 内存过高的问题排查 1.分析OOM异常的原因,堆溢出?栈溢出?本地内存溢出? 2.如果是堆溢出,导出堆dump&…...

华为WLAN实验继续-2,多个AP如何部署
----------------------------------------如果添加新的AP,如何实现多AP的服务----------- 新增加一个AP2启动之后发现无法获得IP地址 在AP2上查看其MAC地址,并与将其加入到AC中去 打开AC,将AP2的MAC加入到AC中 sys Enter system view, re…...
手把手教你写Java项目(1)——流程
个人练手项目的一般流程: 个人练手项目的流程通常相对简单和灵活,但仍然遵循一定的步骤来确保项目的顺利进行。流程相对较为详细,不是所有流程都要实现,一些仅供参考。主要是让大家对项目有初步的了解,不至于无法入手…...
微信小程序post请求
一、普通请求 wx.request({url: http://43.143.124.247:8282/sendEmail,method: POST,data: {user: that.data.currarr[0][that.data.mulu[0]] that.data.currarr[1][that.data.mulu[1]] that.data.sushe,pwd: 3101435196qq.com},header: {Content-Type: application/x-www-…...
frm一级4个1大神复习经验分享系列(二)
先说一下自己的情况,8月份中旬开始备考,中间一直是跟着网课走,notes和官方书都没看,然后10月份下旬开始刷题一直到考试。下面分享一些自己备考的经验和走过的弯路。 一级 一级整体学习下来的感受是偏重于基础的理论知识。FRM一级侧…...
理解磁盘分区与管理:U启、PE、DiskGenius、MBR与GUID
目录 U启和PE的区别: U启(U盘启动): PE(预安装环境): 在DiskGenius中分区完成之后是否还需要格式化: 1.建立文件系统: 2.清除数据: 3.检查并修复分区: 分区表格式中,MBR和GUID的区别: 1…...

GPT-4o和GPT-4有什么区别?我们还需要付费开通GPT-4?
GPT-4o 是 OpenAI 最新推出的大模型,有它的独特之处。那么GPT-4o 与 GPT-4 之间的主要区别具体有哪些呢?今天我们就来聊聊这个问题。 目前来看,主要是下面几个差异。 响应速度 GPT-4o 的一个显著优势是其处理速度。它能够更快地回应用户的查…...