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

相关推荐
苹果醋310 分钟前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader30 分钟前
深入解析 Apache APISIX
java·apache
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
菠萝蚊鸭1 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪1 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0071 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生1 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss1 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm
一棵星2 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言
鲤籽鲲2 小时前
C# Random 随机数 全面解析
android·java·c#