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,然后再执行处理逻辑,如下代码所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/99543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在MySQL中使用!=还能走索引吗?

在MySQL中使用!还能走索引吗? 一般情况下,我们会在一个索引上较多的使用等值查询或者范围查询,此时索引大多可以帮助我们极快的查询出我们需要的数据。 那当我们在where条件中对索引列使用!查询,索引还能发挥他的作用吗&#xf…

vue3 中使用 echarts 图表——准备篇

我们常常在项目中使用图表来表示数据,而目前最常用的图标就是echarts,接下来我们就开始学习在vue中使用echarts图标。 一、准备一个vue项目(一般通过vite来构建,而不是vue-cli) 1.找到打开vite官网 2. 运行创建命令 …

英语字典的一些 关键字 解释:

1. specialized :专业术语 这里的specialized 后面还接了 finance & economics 含义是 在经济和金融的领域,principal 作为一个专业术语,含义是 “本金,可生息资本” 2. XXXXXX

Web1.0——Web2.0时代——Web3.0

Web1.0 Web1.0是互联网的早期阶段,也被称为个人电脑时代的互联网。在这个阶段,用户主要通过web浏览器从门户网站单向获取内容,进行浏览和搜索等操作。在这个时代,技术创新主导模式、基于点击流量的盈利共通点、门户合流、明晰的主…

Vue-1.8生命周期

Vue生命周期 一个Vue实例从创建到销毁的整个过程。 生命周期: 1)创建:响应式数据 ->发送初始化渲染请求 2)挂载:渲染数据->操作dom 3)更新:数据修改,更新视图 4&#xf…

028.Python面向对象_类补充_元类

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉&…

【tomcat、java】

java&#xff1a;maven配置 1.安装插件 <build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port&…

一种重要的天然氨基酸L-Homopropargylglycine(HPG)|CAS:942518-19-6

产品简介&#xff1a;L-Homopropargylglycine是一种重要的天然氨基酸&#xff0c;具有多种生物活性和医学应用价值。它广泛应用于生物学、药学、化学等多个领域。在生物学中&#xff0c;HPG被用作蛋白质合成的标记物&#xff0c;可以通过其特殊的化学反应与蛋白质中的半胱氨酸残…

【SV中的多线程fork...join/join_any/join_none】

SV中fork_join/fork_join_any/fork_join_none 1 一目了然1.1 fork...join1.2 fork...join_any1.3 fork...join_none 2 总结 SV中fork_join和fork_join_any和fork_join_none; Note: fork_join在Verilog中也有&#xff0c;只有其他的两个是SV中独有的&#xff1b; 1 一目了然 1.…

操作系统 OS

本文章是学习《操作系统》慕课版 和 王道《2024年 操作系统 考研复习指导》后所做的笔记&#xff0c;其中一些图片来源于学习资料。 目录 概念&#xff08;定义&#xff09; 目标 方便性 有效性 可扩充性 开放性 作用 OS 作为用户与计算机硬件系统之间的接口 — 人机交…

仅用61行代码,你也能从零训练大模型

本文并非基于微调训练模型&#xff0c;而是从头开始训练出一个全新的大语言模型的硬核教程。看完本篇&#xff0c;你将了解训练出一个大模型的环境准备、数据准备&#xff0c;生成分词&#xff0c;模型训练、测试模型等环节分别需要做什么。AI 小白友好~文中代码可以直接实操运…

Puppeteer基础知识(一)

Puppeteer基础知识&#xff08;一&#xff09; Puppeteer基础知识&#xff08;一&#xff09;一、简介二、其他一些自动化测试工具三、安装与使用四、Puppeteer常用命令五、常见问题解决&#xff1a; 一、简介 Puppeteer 是一个强大而灵活的工具&#xff0c;可以用于网页爬虫、…

Transformer预测 | Pytorch实现基于Transformer 的锂电池寿命预测(CALCE数据集)

文章目录 效果一览文章概述模型描述程序设计参考资料效果一览 文章概述 Pytorch实现基于Transformer 的锂电池寿命预测,环境为pytorch 1.8.0,pandas 0.24.2 随着充放电次数的增加,锂电池的性能逐渐下降。电池的性能可以用容量来表示,故寿命预测 (RUL) 可以定义如下: SOH(t…

RTC 时间、闹钟

实时时钟RTC是一个独立的定时器。RTC模块拥有一个连续计数的计数器&#xff0c;在软件配置下&#xff0c;可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。 在掉电情况下 RTC仍可以独立运行 只要芯片的备用电源…

实现协议互通:探索钡铼BL124EC的EtherCAT转Ethernet/IP功能

钡铼BL124EC是一种用于工业网络通信的网关设备&#xff0c;专门用于将EtherCAT协议转换成Ethernet/IP协议。它充当一个桥梁&#xff0c;连接了使用不同协议的设备&#xff0c;使它们能够无缝地进行通信和互操作。 具体来说&#xff0c;BL124EC通过支持EtherCAT&#xff08;以太…

2023,全网最真实的自动化测试学习路线,看不懂来打我!

随着测试行业的发展&#xff0c;“会代码”越来越成为测试工程师的一个标签。打开各大招聘网站&#xff0c;测试工程师月薪一万以上基本都有一个必备技能&#xff0c;那就是自动化测试。那么自动化测试到底难不难呢&#xff1f;下面我将会将我的经历讲给大家听&#xff0c;希望…

【java基础学习】之DOS命令

#java基础学习 1.常用的DOS命令&#xff1a; dir:列出当前目录下的文件以及文件夹 md: 创建目录 rd:删除目录cd:进入指定目录 cd.. :退回到上级目录 cd\ : 退回到根目录 del:删除文件 exit:退出dos命令行 1.dir:列出当前目录下的文件以及文件夹 2.md: 创建目录 …

[NewStarCTF 2023 公开赛道] week1 Crypto

brainfuck 题目描述&#xff1a; [>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<-]>>>>>>>.>----.<-----.>-----.>-----.<<<-.>>..…

黑豹程序员-架构师学习路线图-百科:AJAX

文章目录 1、什么是AJAX2、发展历史3、工作原理4、一句话概括 1、什么是AJAX Ajax即Asynchronous&#xff08;呃森可乐思&#xff09; Javascript And XML&#xff08;异步JavaScript和XML&#xff09; 在 2005年被Jesse James Garrett&#xff08;杰西詹姆斯加勒特&#xff09…

Android Studio新建项目缓慢解决方案

关于Android Studio2022新建项目时下载依赖慢的解决方案 起因解决方案gradle下载慢解决方案kotlin依赖下载慢解决方案 结尾 起因 新建Android Studio项目时&#xff0c;常会因为网络问题导致部分依赖下载缓慢&#xff0c;其中gradle和kotlin最拖慢进度。 解决方案 gradle下载…