Spring中的设计模式
目录
工厂模式
组合模式
适配器模式
代理模式
单例模式
观察者模式
模板方法模式
责任链模式
- Spring有着非常优雅的设计,很多地方都遵循SOLID原则,里面的设计模式更是数不胜数
- 大概有以下几种:
-
工厂模式
- 所谓的工厂模式,核心是屏蔽内部的实现,直接由client使用即可
- Spring的IOC就是一个非常好的工厂模式的例子
- Spring IOC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的
- IOC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁
- Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象
- 两者对比:
- BeanFactory:延迟注入(使用到某个 bean 的时候才会注入),相比于ApplicationContext来说会占用更少的内存,程序启动速度更快
- ApplicationContext:容器启动的时候,不管你用没用到,一次性创建所有 bean
- BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory,除了有BeanFactory的功能还有额外更多功能,所以一般开发人员使用ApplicationContext会更多
-
组合模式
- 组合模式在Spring MVC中用的非常多,其中的参数解析,响应值处理等模块就是使用了组合模式
- 拿参数解析模块举例:
- 类图如下:
- 可以发现,整体的参数解析模块中,由一个接口 HandlerMethodArgumentResolver 负责
- 其中父节点会实现该接口,同时对所有的具体的子接口进行聚合
- 其实这个里面不止用了组合模式,接口还提供了 #supportsParamerter 方法,去判断是否执行该resolver,这也是策略模式的一种
-
适配器模式
- 适配器模式简而言之就是上游为了适应下游,而要做一些适配,承担适配工作的模块,就叫做适配器
- 常见的场景是甲方因为话语权很高,提供了一套交互模型,而所有对接甲方模型的乙方,就需要通过适配器模式来适配甲方的模型和自己已有的系统
- 在Spring MVC中,HandlerAdapter 就是典型的适配器模式
- 参考其注释我们可以发现:
- 对于DispatcherServlet来说,HandlerAdapter是核心的业务逻辑处理流程,DispatcherServlet只负责调用 HandlerAdapter#handle 方法即可
- 至于当前Http的请求该如何处理,则交给HandlerAdapter的实现方负责
- 换句话说,HandlerAdapter只是定义了和DispatcherServlet交互的标准,帮助不同的实现适配了DispatcherServlet而已
- 譬如,用于Controller注解解析和url映射的逻辑就是通过 RequestMappingHandlerAdapter 实现的
- Spring AOP中的适配器模式
- 我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter
- Advice 常用的类型有:BeforeAdvice(目标方法调用前,前置通知)、AfterAdvice(目标方法调用后,后置通知)、AfterReturningAdvice(目标方法执行结束后,return之前)等等
- 每个类型Advice(通知)都有对应的拦截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter、AfterReturningAdviceInterceptor
- Spring预定义的通知要通过对应的适配器,适配成 MethodInterceptor 接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice)
- Spring MVC中的适配器模式
- 在Spring MVC中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler
- 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理
- HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类
- 为什么要在 Spring MVC 中使用适配器模式?
- Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理
- 如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要自行来判断,像下面这段代码一样:
- 假如我们再增加一个 Controller类型就要在上面代码中再加入一行判断语句,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭
-
代理模式
- 代理模式和适配器模式的核心区别就在于,适配器模式的目的是为了适配不同的场景,而代理模式的目的则是enhance,即增强被代理的类(如增加日志打印功能等)
- Spring的AOP就是代理模式的典型代表,Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
- AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性
- 如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
- 当然你也可以使用 AspectJ,Spring AOP 已经集成了AspectJ
- Spring AOP 属于运行时增强,而 AspectJ 是编译时增强
- Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)
- AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单
- 如果我们的切面比较少,那么两者性能差异不大
- 但是,当切面太多的话,最好选择 AspectJ,它比 Spring AOP 快很多
-
单例模式
- 单例模式是Spring一个非常核心的功能,Spring中的bean默认都是单例的,这样可以尽最大程度保证对象的复用和线程安全
- Spring Bean也不止是单例的,还有其他作用域,如下:
- prototype:每次获取都会创建一个新的 bean 实例;也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例
- request(仅 Web 应用可用):每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效
- session(仅 Web 应用可用):每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效
- global-session(仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效
- websocket(仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean
- Spring 通过 ConcurrentHashMap 实现单例注册表的特殊方式实现单例模式
- Spring 实现单例的核心代码如下:
-
观察者模式
- 观察者模式是一种对象行为型模式
- 它表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应
- 当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener
- Spring 事件驱动模型就是观察者模式很经典的一个应用
- Spring 事件驱动模型非常有用,在很多场景都可以解耦我们的代码
- 比如我们每次添加商品的时候都需要重新更新商品索引,这个时候就可以利用观察者模式来解决这个问题
- Spring 事件驱动模型中的三种角色
- 事件角色
- ApplicationEvent(org.springframework.context包下)充当事件的角色,这是一个抽象类,它继承了java.util.EventObject并实现了 java.io.Serializable 接口
- Spring 中默认存在以下事件,他们都是对 ApplicationEvent 的实现(继承自ApplicationEvent):
- ContextStartedEvent:ApplicationContext 启动后触发的事件
- ContextStoppedEvent:ApplicationContext 停止后触发的事件
- ContextRefreshedEvent:ApplicationContext 初始化或刷新完成后触发的事件
- ContextClosedEvent:ApplicationContext 关闭后触发的事件
- 事件监听者角色
- ApplicationListener 充当了事件监听者角色,它是一个接口,里面只定义了一个 onApplicationEvent() 方法来处理ApplicationEvent
- ApplicationListener 接口类源码如下,可以看出接口定义看出接口中的事件只要实现了 ApplicationEvent 就可以了
- 所以,在 Spring 中我们只要实现 ApplicationListener 接口实现 onApplicationEvent() 方法即可完成监听事件
- 事件发布者角色
- ApplicationEventPublisher 充当了事件的发布者,它也是一个接口
- ApplicationEventPublisher 接口的 publishEvent() 这个方法在 AbstractApplicationContext 类中被实现,阅读这个方法的实现,你会发现实际上事件真正是通过 ApplicationEventMulticaster 来广播出去的;具体内容过多,就不在这里分析了
- ApplicationEventPublisher 充当了事件的发布者,它也是一个接口
- Spring 的事件流程总结
- 1-定义一个事件:实现一个继承自 ApplicationEvent,并且写好相应的构造函数
- 2-定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法
- 3-使用事件发布者发布消息:可以通过 ApplicationEventPublisher 的 publishEvent() 方法发布消息
- Example:
- 当调用 DemoPublisher 的 publish() 方法的时候,比如 demoPublisher.publish("你好"),控制台就会打印出:接收到的信息是:你好
- 事件角色
-
模板方法模式
- 用来解决代码重复的问题
- 模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中
- 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式
- Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式
- 一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性
- 如果使用过Spring的事务管理,相信一定对 TransactionTemplate 这个类不陌生,而且顾名思义,这个也是用到了模板方法
- 它把事务操作按照3个固定步骤来写:
- 1. 执行业务逻辑
- 2. 如果异常则回滚事务
- 3. 否则提交事务如下代码所示:
-
责任链模式
- 对于Spring MVC来说,他会通过一系列的拦截器来处理请求执行前,执行后,以及结束的response,核心的类是 handlerExecutionChain,它封装了 HandlerAdapter 和一系列的过滤器
- 对于执行前的处理来说,DispatherServlet会先通过 handlerExecutionChain 获取所有的 HandlerInterceptor,然后再执行处理逻辑,如下代码所示:
相关文章:

Spring中的设计模式
目录 工厂模式 组合模式 适配器模式 代理模式 单例模式 观察者模式 模板方法模式 责任链模式 Spring有着非常优雅的设计,很多地方都遵循SOLID原则,里面的设计模式更是数不胜数大概有以下几种: 工厂模式 所谓的工厂模式,核…...
软考 系统架构设计师系列知识点之软件质量属性(1)
这个十一注定是一个不能放松、保持“紧”的十一。由于报名了全国计算机技术与软件专业技术资格(水平)考试,11月4号就要考试,因此8天长假绝不能荒废,必须要好好利用起来。现在将各个核心知识点一一进行提炼并做记录。 所…...

GPT系列论文解读:GPT-1
GPT系列 GPT(Generative Pre-trained Transformer)是一系列基于Transformer架构的预训练语言模型,由OpenAI开发。以下是GPT系列的主要模型: GPT:GPT-1是于2018年发布的第一个版本,它使用了12个Transformer…...

数学分析:含参变量的积分
同样很多收敛性的证明不是重点,但里面的知识还是需要适当掌握,知道中间的大致思考和解决路径即可。 本质还是极限的可交换性,求导可以换到积分里面去操作。 这里要注意变量的区别,首先积分的被积变量是x,但是函数的变量…...
关于一篇ElementUI之CUD+表单验证
目录 一.CUD增删改查简述 1.1.增删改功能实现 二.表单验证 前端所有代码: 好啦今天就分享到这了,希望能帮到你哦!!! 以下的代码基于我博客中的代码进行续写 : 关于ElementUI之动态树数据表格分页实例 一.CUD增删改…...
VUE模板编译的实现原理
前言 在Vue.js 2.0中,模板编译是通过将模板转换为渲染函数来实现的。渲染函数是一个函数,它返回虚拟DOM节点,用于渲染实际的DOM。Vue.js的模板编译过程可以分为以下几个步骤: 将模板解析为抽象语法树(AST)…...

基础算法之——【动态规划之路径问题】1
今天更新动态规划路径问题1,后续会继续更新其他有关动态规划的问题!动态规划的路径问题,顾名思义,就是和路径相关的问题。当然,我们是从最简单的找路径开始! 动态规划的使用方法: 1.确定状态并…...

三十三、【进阶】索引的分类
1、索引的分类 (1)总分类 主键索引、唯一索引、常规索引、全文索引 (2)InnoDB存储引擎中的索引分类 2、 索引的选取规则(InnoDB存储引擎) 如果存在主键,主键索引就是聚集索引; 如果不存在主键ÿ…...

VBox启动失败、Genymotion启动失败、Vagrant迁移
VBox启动失败、Genymotion启动失败、Vagrant迁移 2023.10.9 最新版本vbox7.0.10、Genymotion3.5.0 Vbox启动失败 1、查看日志 Error -610 in supR3HardenedMainInitRuntime! (enmWhat4) Failed to locate ‘vcruntime140.dll’ 日志信息查看方法->找到虚拟机所在位置->…...

一篇短小精悍的文章让你彻底明白KMP算法中next数组的原理
以后保持每日一更,由于兴趣较多,更新内容不限于数据结构,计算机组成原理,数论,拓扑学......,所谓:深度围绕职业发展,广度围绕兴趣爱好。往下看今日内容 一.什么是KMP算法 KMP&#x…...

CSS盒子定位的扩张
定位的扩展 绝对定位(固定定位)会完全压住盒子 浮动元素不会压住下面标准流的文字,而绝对定位或固定位会压住下面标准流的所有内容 如果一个盒子既有向左又有向右,则执行左,同理执行上 显示隐藏 display: none&…...

SpringBoot整合POI实现Excel文件读写操作
1.环境准备 1、导入sql脚本: create database if not exists springboot default charset utf8mb4;use springboot;create table if not exists user (id bigint(20) primary key auto_increment comment 主键id,username varchar(255) not null comment 用…...
从零开始的力扣刷题记录-第八十七天
力扣每日四题 129. 求根节点到叶节点数字之和-中等130. 被围绕的区域-中等437. 路径总和 III-中等376. 摆动序列-中等总结 129. 求根节点到叶节点数字之和-中等 题目描述: 给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 …...

【1】c++设计模式——>UML类图的画法
UML介绍 UML:unified modeling language 统一建模语言 面向对象设计主要就是使用UML类图,类图用于描述系统中所包含的类以及他们之间的相互关系,帮助人们简化对系统的理解,他是系统分析和设计阶段的重要产物,也是系统编码和测试的…...
SAP UI5 指定 / 变更版本
SAP UI5 指定 / 变更版本 Currently, SAP Fiori tools support SAP Fiori elements and SAPUI5 freestyle projects with minimum SAPUI5 versions 1.65 or higher. In case there’s a need to test an existing projects with a lower SAPUI5 version, the following worka…...
SpringMVC中异常处理详解
单个控制器异常处理 // 添加ExceptionHandler,表示该方法是处理异常的方法,属性为处理的异常类ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class})public String exceptionHandle1(Exception ex, Model mo…...

PPT课件培训视频生成系统实现全自动化
前言 困扰全动自化的重要环节,AI语音合成功能,终于可以实现自动化流程,在此要感谢团队不懈的努力和韧性的精神! 实现原理 请参照我的文章《Craneoffice云PPT课件培训视频生成系统》 基本流程 演示视频 PPT全自动 总结 过去实…...

基于腾讯云的OTA远程升级
一、OTA OTA即over the air,是一种远程固件升级技术,它允许在设备已经部署在现场运行时通过网络远程更新其固件或软件。OTA技术有许多优点,比如我们手机系统有个地方做了优化,使用OTA技术我们就不用召回每部手机,直接通过云端就可…...

如何在VS2022中进行调试bug,调试的快捷键,debug与release之间有什么区别
什么是bug 在学习编程的过程中,应该都听说过bug吧,那么bug这个词究竟是怎么来的呢? 其实Bug的本意是“虫子”或者“昆虫”,在1947年9月9日,格蕾丝赫柏,一位为美国海军工作的电脑专家,也是最早…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式
pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图,如果边框加在dom上面,pdf-lib导出svg的时候并不会导出边框,所以只能在echarts图上面加边框 grid的边框是在图里…...

python可视化:俄乌战争时间线关键节点与深层原因
俄乌战争时间线可视化分析:关键节点与深层原因 俄乌战争是21世纪欧洲最具影响力的地缘政治冲突之一,自2022年2月爆发以来已持续超过3年。 本文将通过Python可视化工具,系统分析这场战争的时间线、关键节点及其背后的深层原因,全面…...