当前位置: 首页 > news >正文

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 来广播出去的;具体内容过多,就不在这里分析了
      • 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&#xff09…...

基础算法之——【动态规划之路径问题】1

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

三十三、【进阶】索引的分类

1、索引的分类 (1)总分类 主键索引、唯一索引、常规索引、全文索引 (2)InnoDB存储引擎中的索引分类 2、 索引的选取规则(InnoDB存储引擎) 如果存在主键,主键索引就是聚集索引; 如果不存在主键&#xff…...

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全自动 总结 过去实…...

Densenet--->比残差力度更大 senet-->本质抑制特征

...

基于腾讯云的OTA远程升级

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

如何在VS2022中进行调试bug,调试的快捷键,debug与release之间有什么区别

什么是bug 在学习编程的过程中,应该都听说过bug吧,那么bug这个词究竟是怎么来的呢? 其实Bug的本意是“虫子”或者“昆虫”,在1947年9月9日,格蕾丝赫柏,一位为美国海军工作的电脑专家,也是最早…...

【kafka】Golang实现分布式Masscan任务调度系统

要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

ES6从入门到精通:前言

ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...