从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)

从Spring源码学习5种设计模式

学号:

姓名:

文章目录

  • 从Spring源码学习5种设计模式
    • 引言
    • 工厂模式
      • Spring中的工厂模式
    • 单例模式
      • Spring中的单例Bean
    • 代理设计模式
      • Spring AOP的动态代理
      • AOP使用例子
    • 观察者模式
      • Spring的事件驱动模型
      • Spring观察者模式使用举例
    • 模板模式
      • Spring事务管理中的模板模式
    • 结语

引言

  • 设计模式(Design Pattern)是软件开发人员在软件开发过程中面临的一般问题的解决方案,是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结,通俗来讲就是程序员面对软件工程设计问题总结出来的有用的经验。使用合适的设计模式可以提高代码可读性、重用性和可靠性,可以降低软件的复杂度,提高系统的通用性和扩展性,学习设计模式可以帮助经验不足的开发人员通过简单快捷的方式学习软件设计。

  • 根据设计模式参考书《设计模式:可复用面向对象软件的基础》,常见的设计模式有23种,分为三大类:创建型模式、结构型模式和行为型模式。

    • 创建型模式从代码中移除了对需要实例化的具体类的引用,增强了对象和类之间的独立性,使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。创建型模式分为类创建模式和对象创建模式,包括5种:抽象工厂模式、建造者模式、工厂方法模式、原型模式和单例模式,除了工厂模式是类创建模式,其他均为对象创建模式。
    • 结构型模式描述如何把类或者对象按照某种布局组成更大的结构,分为结构型类模式和结构型对象模式,其中类结构型模式通过继承来组织接口和类,对象结构型模式描述如何对一组对象进行组合来实现新功能。结构型模式包括7种:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式,除了适配器模式分为类结构型模式和对象结构型模式两种,其他均属于结构型对象模式。
    • 行为型模式描述程序在运行时复杂的流程控制,关注对象之间的通信,涉及算法和对象之间职责的分配,描述多个类和对象之间如何相互协作共同完成单个对象无法完成的任务。行为型模式分为类行为型模式和对象行为模式,类行为型模式采用继承机制在类之间分派行为,对象行为模式则通过组合或聚合在对象之间分配行为。行为型模式是最大的一类,包含11种设计模式,分别为模版方法模式、解释器模式、状态模式、策略模式、责任链模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式,除了模板方法模式和解释器模式是类行为型模式,其他均属于对象行为型模式。
  • Spring作为最受欢迎的企业级 Java 应用程序开发框架,大量使用了设计模式来进行功能扩展,从这款优秀的开源框架中学习设计模式是很好的选择。

  • 本文将分析Spring框架对设计模式的使用,帮助软件开发人员加深对设计模式落地应用的理解,写出让人惊叹的代码。对于每种设计模式,本文首先给出相关模式的思想,随后附以其在Spring中的应用解释。

工厂模式

  • 工厂模式(Factory Pattern)将类的实例化延迟到子类,定义一个接口来创建对象,让子类决定实例化哪个类。

  • 其类图如下:

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第1张图片

    • Product是一个接口,所有产品必须实现此接口,其他类使用产品时通过引用此接口来使用产品。
    • ConcreteProduct是具体产品产品类。
    • Creator类实现了所有操纵产品的方法,但不实现工厂方法。
    • ConcreteCreator类实现factoryMethod()方法,来制造出实际产品ConcreteProduct
  • 工厂模式适用于多种场景:

    • 无法确定对象的类别以及依赖关系时;
    • 当一个类希望由其子类来指定它所创建的对象时;
    • 希望用户可以扩展软件库或框架的内部组件时;
    • 希望复用现有对象节省系统资源时;

Spring中的工厂模式

  • 工厂方法模式是比较常见的设计模式,Spring底层大量使用这种设计模式进行封装。

  • Spring使用工厂模式,可以通过BeanFactoryApplicationContext 创建 bean 对象。

  • **在此以FactoryBean为例,从它开始阅读Spring源码,探索其工厂模式的使用。**代码阅读顺序以继承、实现关系为依据:

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第2张图片

FactoryBean

  • org.springframework.beans.factory.FactoryBean

  • FactoryBean既是Bean,也能生产Bean,只提供了3个方法。

    • getObject()方法返回真正的Bean实例。
    • getObjectType()返回该Factory生产的Bean class。
    • isSingleton()判断该Factory生成的Bean是否为单例。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第3张图片

补充:后面单例模式分析时涉及BeanFactory,在此说明二者区别。BeanFactory是IOC容器或对象工厂,在Spring中所有的BeanBeanFactory管理。FactoryBean是个Bean,但也能够生产或修饰工厂Bean。

(4条消息) spring中BeanFactory和FactoryBean的区别_常胜的博客-CSDN博客_factorybean和beanfactory的区别

AbstractFactoryBean

  • org.springframework.beans.factory.config.AbstractFactoryBean

  • AbstractFactoryBean是一个实现了FactoryBean接口的抽象类,可以基于此类定义自己的FactoryBean

  • AbstractFactoryBean中的getObject()方法如下:

    public final T getObject() throws Exception {
        if (this.isSingleton()) { //如果是单例模式,单例已经初始化则返回单例,否则返回earlySingletonInstance
            return this.initialized ? this.singletonInstance : this.getEarlySingletonInstance();
        } else {
            return this.createInstance();//不是单例模式,则创建对象
        }
    }
    
  • 其中不是单例模式时,调用的createInstance()方法是AbstractFactoryBean中的一个抽象方法,表示由其子类自定义创建对象的逻辑。

    protected abstract T createInstance() throws Exception;
    

    查看继承了AbstractFactoryBean的工厂类如下,选取AbstractServiceLoaderBasedFactoryBean类继续阅读

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第4张图片

AbstractServiceLoaderBasedFactoryBean

  • org.springframework.beans.factory.serviceloader.AbstractServiceLoaderBasedFactoryBean

  • 此类对父类AbstractFactoryBean的抽象方法createInstance加以实现,创建实例时调用的getObjectToExpose()仍为抽象方法,继续查看该类的子类ServiceLoaderFactoryBean

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第5张图片

ServiceLoaderFactoryBean

  • org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean

  • 该类中实现的getObjectToExpose()方法返回了serviceLoader,因此得出结论:该FactoryBean工厂生产了ServiceLoader实例。

从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第6张图片

  • Spring框架中各个类的关系复杂,以上分析我们可以认为,FactoryBean为真正的抽象工厂类,getObject()为其工厂方法,AbstractServiceLoaderBasedFactoryBean是一种具体工厂,但此具体工厂又可能生产多种产品。

单例模式

  • 单例模式(Singleton Pattern)保证一个类只有一个实例,并提供一个访问该实例的全局访问点。

  • 其类图如下:

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第7张图片

    • 单例模式的类可以是一般的类,具有一般数据和方法。
    • uniqueInstance为该类持有的唯一单件实例。
    • getInstance()为静态方法,可以在代码的任何地方使用Singleton.getInstance()访问它,可以延迟实例化。
  • 使用场景

    • 一个类只能有一个实例时。在系统中有些对象只需要一个即可,比如缓存、对话框、注册表、日志对象、线程池、充当打印机、显卡等设备驱动程序的对象。
    • 唯一实例应该通过子类化可扩展,且客户无需更改代码就可以使用一个扩展实例时;
    • 需要更加严格地控制全局变量时;

Spring中的单例Bean

  • Spring中bean的默认作用域单例的,实现单例的方式有2种

    • xml形式:

      <bean id="user" class="com.buaa.mmy.User" scope="singleton"/>
      
    • 注解形式@Scope(value = "singleton")

  • Spring 中加载单例的过程在BeanFactory接口中的getBean()方法中定义的,实现默认在AbstractBeanFactory类中,该类的getBean()实现方法调用doGetBean()方法,该方法的大致流程为

    ①尝试从缓存获取Bean

    ②判断循环依赖

    ③递归到父容器获取Bean实例

    ④从当前容器获取BeanDefinition实例

    ⑤递归实例化显示依赖的Bean

    ⑥根据不同的Scope采取不同策略创建Bean实例

    ⑦对Bean进行类型检查

从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第8张图片

  • Object sharedInstance = getSingleton(beanName);尝试从缓存集合中获取Bean实例。若先前创建过Bean实例且调用的getBean方法传入的参数为空,则会执行doGetBean()方法里if中的逻辑,否则执行else中的代码。getSingleton()方法调用如如下方法:

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第9张图片

    • 首先通过名字查找单例bean是否存在,若不存在且正在创建,就查看缓存中是否存在,若仍不存在则允许懒加载。
    • 在首次调用时,isSingletonCurrentlyInCreation()结果为false因此getSingleton()方法会返回null,doGetBean()方法继续运行。
  • 此时sharedInstance=null,缓存中不存在Bean,执行else部分,核心代码如下图,会再次调用getSingleton()重载方法。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第10张图片

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第11张图片

  • 核心代码如下,通过 ConcurrentHashMap 实现单例注册表的特殊方式实现单例模式。当从缓存中加载单例对象时,只要创建一个单例对象就会把它放入singletonObjects中,在getBean()时只能获取一个。创建了单例Bean的缓存之后,下次直接从缓存中可以获取到Bean。

    // 通过线程安全的ConcurrentHashMap实现单例注册表
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized(this.singletonObjects) {
            // 检查缓存中是否存在实例 
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //...
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } 
                //...
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    this.afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    //如果实例对象不存在,在singletonObject中注册单例
                    this.addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }
    
  • 以上为Spring创建单例的过程,由于处理时需要解决其他问题,因而显得十分复杂,但本质逻辑较为清楚。

(4条消息) doGetBean方法详解_kerry_x的博客-CSDN博客

代理设计模式

  • 代理模式(Proxy Pattern)为另一个对象提供一个替身或占位符,从而控制对这个对象的访问。

  • 其类图如下

    • SubjectProxyRealSubject提供了接口,通过实现同一个接口,Proxy可以在RealSubject出现的地方取代它。
    • RealSubject通常是真正做事的对象。
    • Proxy持有Subject的引用,必要时可以将请求转发给SubjectProxy通常负责创建RealSubject,并控制对RealSubject的访问。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第12张图片

  • 使用场景如下

    • 延迟初始化,虚拟代理:需要创建 一个偶尔使用的重量级服务对象时。
    • 访问控制,保护代理:只希望特定客户端使用服务对象时,控制对原始对象的访问,保护代理用于对象应有不同的访问权限的情景
    • 本地执行远程服务,远程代理: 适用于服务对象位于远程服务器上的情形,为一个对象在不同的地址空间提供局部代表。
    • 记录日志请求,日志记录代理:适用于需要保存对于服务对象的请求历史记录时。
    • 缓存请求结果,缓存代理:适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。
    • 智能引用。取代简单的指针,在访问对象时可执行一些附加操作。在没有客户端使用某个重量级对象时立即销毁该对象,第一次引入一个之久对象时将它装入内存,访问一个实际对象前检查是否以及锁定。
  • 根据代理类生成的时间不同可以把代理分为静态代理和动态代理。

    • 静态代理是由程序员创建或工具生成代理类的源码,再编译代理类,静态即在程序运行前就以及存在代理类的字节码文件,代理类和委托类的关系在运行前已经确定。
    • 动态代理的源码是在程序运行期间由JVM根据反射等机制动态生成,不存在代理类的字节码文件,代理类和委托类的关系在程序运行时确定。动态代理又划分为基于接口的动态代理(JDK API动态代理)和基于类的动态代理(cglib动态代理)。

Spring AOP的动态代理

  • Spring中AOP的实现方式是动态代理。若要代理的对象实现了某个接口,Spring AOP采用JDK Proxy创建代理对象;若要代理的对象没有实现该接口,则采用cglib生成一个被代理对象的子类来作为代理。也可以强制使用cglib方式,在xml配置或采用@EnableAspectJAutoProxy(proxyTargetClass = true)即可。

  • 在Spring中,创建代理的过程如下:

  • 从Spring 容器处理器固定方法AbstractAutoProxyCreator定义的 postProcessAfterInitialization()方法开始,当每个bean初始化完,都会执行该方法:

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第13张图片

  • postProcessAfterInitialization 中,获取完用于缓存的key后,进入到 wrapIfNecessary() 方法进行具体包装。

    • 该方法首先判断不代理的情况:
      • targetSourcedBeans中含有beanName,说明beanName的代理对象已经生成;
      • adviseBeans中存储是否需要为某个Bean生成代理,如果里面存在cacheKey,说明之前已经初始化过Bean的代理类,此时不需要再次初始化;
      • 是框架方法或者应该跳过时也不进行代理。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第14张图片

    • 其他情况下,首先通过getAdvicesAndAdvisorsForBean()获取所有过滤器,接下来通过createProxy()创建代理过程。
  • createProxy()方法中为创建代理的过程。

    • 先设置该bean的originalTargetClass属性,创建代理工厂。
    • 设置代理规则是使用JDK还是cglib代理。
    • 把advice类型的增强包装成advisor切面,将先前获取的过滤后的specificInterceptors转换为advisor,添加增强。
    • 设置freezen属性控制代理配置后是否允许修改代理值。
    • 最终代理工厂通过getProxy()方法获取代理实例。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第15张图片

  • 创建代理的过程被Spring委托给ProxyFactory处理,在对ProxyFactory进行初始化的过程中,有一部分创建AOP代理工厂的过程。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第16张图片

  • 查看DefaultAopProxyFactorycreateAopProxy 方法,在此判断了使用何种代理。代理配置会影响Spring使用代理的方式,此外,如果目标对象实现了接口,默认采用JDK动态代理,但也可以强制使用cglib实现AOP,若没有实现接口则必须采用cglib实现。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第17张图片

AOP使用例子

  • 使用AOP可以通过代理的方式为程序添加统一功能,在权限、缓存、内容传递、错误处理、日志、追踪、事务、同步、持久化等场景中均可使用。

  • 在本学期J2EE大作业的Web开发中,为了方便调试,需要查看接口调用的基本信息,可以采用AOP实现。打印内容包括接口、请求方式、输入参数、返回值等。

  • 首先在pom.xml中导入所需依赖

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-aopartifactId>
    dependency>
    
    <dependency>
        <groupId>com.google.code.gsongroupId>
        <artifactId>gsonartifactId>
        <version>2.8.5version>
    dependency>
    
  • 切面类配置如下

    package com.example.common;
    
    import com.google.gson.Gson;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Aspect //标识切面类
    @Component
    public class WebLogAspect {
        private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
        
        //以 controller 包下定义的所有请求为切入点
        @Pointcut("execution(public * com.example.controller..*.*(..))")
        public void webLog() {
        }
    
        @Before("webLog()") //在切入点之前织入
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            // 开始打印请求日志
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 打印请求相关参数,依次为接口方法,接口URL,请求参数,调用者ip
            logger.info("{}  {}  {}  {}", request.getMethod(), request.getRequestURL().toString(), new Gson().toJson(joinPoint.getArgs()), request.getRemoteAddr());
        }
    
        @After("webLog()")//在切点之后织入
        public void doAfter() throws Throwable {
        }
    
        @Around("webLog()") //环绕方式
        public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            Object result = proceedingJoinPoint.proceed();
            // 打印出参
            logger.info("Response Args  : {}", new Gson().toJson(result));
            return result;
        }
    }
    
  • 最终效果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e03yNlfc-1657336764392)(img/Spring框架中的设计模式_img/image-20220615150748755.png)]

观察者模式

  • 观察者模式(Observer Pattern)定义对象之间的一种多对多依赖关系,当一个对象的状态法发生改变时,所有依赖于它的对象都得到通知并被自动更新。

  • 类图如下

    • Subject是一个主题接口,对象使用这个接口注册为观察者,或者把自己从观察者中删除。
    • ConcreteSubject是一个具体的主题,它总是实现主题接口,除了注册、删除方法之外,具体实现了notifyObserver()方法,用于在观察者状态改变时更新当前所有观察者。此外,具体主题类也可能有设置和获取状态的方法。
    • Observer是一个观察者接口,所有潜在的观察者必须实现此接口,该接口只有一个update()方法,当主题状态改变时被调用。
    • ConcreteObserver是具体的观察者,必须注册具体主题,以便于接收更新消息。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第18张图片

  • 应用场景

    • 当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时,可使用观察者模式。
    • 当应用中的一些对象必须观察其他对象时,可使用此模式。 但仅能在有限时间内或特定情况下使用。

Spring的事件驱动模型

  • Spring 事件驱动模型是观察者模式很经典的一个应用。该模型可以解耦我们的代码。比如我们每次添加商品的时候都需要重新更新商品索引,可以利用观察者模式来解决这个问题。

  • Spring的观察者模式中包含几个角色:事件、事件监听、事件发布。

  • 事件角色ApplicationEvent (org.springframework.context包下)充当,作为一个抽象类它继承java.util.EventObject并实现了 java.io.Serializable接口。所有的事件都需要继承 ApplicationEvent, 并且通过source得到事件源。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第19张图片

    Spring中默认存在以下事件,它们是对抽象类ApplicationContextEvent的具体实现。

    • ContextRefreshedEventApplicationContext 初始化或刷新时触发的事件。“初始化”意味着所有 bean 加载,post-processor bean 被检测到并且激活,单例预先实例化,ApplicationContext 对象可以使用了。只要上下文没有关闭,可以触发多次刷新, ApplicationContext 提供了一种可选择的支持这种“热”刷新。
    • ContextStartedEventApplicationContext 开始使用ConfigurableApplicationContext接口 start() 方法时触发的事件。“开始”意味着所有生命周期 bean 接收到一个明确的起始信号。这个信号通常用于明确停止后重新启动,也可以用于启动组件没有被配置为自动运行。
    • ContextStoppedEventApplicationContext 停止时使用 ConfigurableApplicationContext 接口上的 stop() 方法时触发的事件。“停止”意味着所有生命周期bean接收一个显式的停止信号。
    • ContextClosedEvent ApplicationContext 关闭时触发的事件。“关闭”意味着所有单例 bean 被摧毁,不能刷新或重启。
  • 事件监听者角色ApplicationListener 充当。它是一个只定义了 onApplicationEvent()方法的接口,用于处理ApplicationEvent。在 Spring中我们只要实现 ApplicationListener 接口的 onApplicationEvent() 方法即可完成监听事件。

  • 事件发布者角色ApplicationEventPublisher充当。ApplicationEventPublisher 接口的publishEvent()方法在AbstractApplicationContext类中被实现。

Spring观察者模式使用举例

  • 在Spring中实现观察者模式的流程如下:
    • 自定义事件类:继承 ApplicationEvent,写相应的构造函数;
    • 自定义事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法;
    • 使用事件发布者发布消息:可以通过 ApplicationEventPublisherpublishEvent() 方法发布消息。
@Component
public class MyEvent extends ApplicationEvent {
    public MyEvent(ApplicationContext source) {
        super(source);
        System.out.println("MyEvent 构造器执行");
    }
    
    public void echo() {
        System.out.println("模拟业务逻辑执行");
    }
}
@Component
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        System.out.println("监听到了...");
        myEvent.echo();
    }
}
@Component
public class MyPublisher implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    //发布事件  监听该事件的监听者都可以获取消息
    public void publisherEvent(MyEvent myEvent) {
        System.out.println("---开始发布 myEvent 事件---");
        applicationContext.publishEvent(myEvent);
    }
}
// 单元测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTest {
    @Autowired
    private MyPublisher myPublisher;
    @Autowired
    private MyEvent myEvent;

    @Test
    public void contextLoads() {
        myPublisher.publisherEvent(myEvent);
    }
}

单元测试结果✔

从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第20张图片

模板模式

  • 模板模式(Template Pattern)定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

  • 类图如下

    • AbstractClass抽象类中包含模板方法,该模板方法实现的过程中用到两个原语操作,primitiveOperaton1()primitiveOperaton2(),模板方法本身和这两个操作的具体实现之间被解耦。
    • ConcreteClass为具体类,可能有多个,每个都实现了模板方法所需的全部操作,当模板方法需要时会调用。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第21张图片

  • 使用场景为

    • 只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时,可使用模板方法模式。
    • 当多个类的算法除一些细微不同之外几乎完全一样时。但后果是只要算法发生变化,可能需要修改所有的类。

Spring事务管理中的模板模式

  • Spring底层对模板模式的使用有多处,比如 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,事务管理器中的类等等。

  • AbstractPlatformTransactionManager为例,此类中有多个抽象方法。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第22张图片

    doGetTransaction()方法为例,在模板方法getTransaction()中调用了此抽象方法,而该抽象方法的具体实现由子类完成。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第23张图片

    其中AbstractPlatformTransactionManager类的子类DataSourceTransactionManager对抽象方法doGetTransaction()的实现如下。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第24张图片

    由于AbstractPlatformTransactionManager类有多个子类,不同子类对象的getTransaction()使用方法略有差别,通过模板设计模式,保留公共部分,抽象出有差异的地方施以不同实现,大大提高了代码复用性✨。

    从Spring源码学习设计模式(工厂、单例、代理、观察者、模板)_第25张图片

结语

  • 结束之前再回顾一下本文提到的设计模式的思想

    • 工厂模式将类的实例化延迟到子类,定义一个接口来创建对象,让子类决定实例化哪个类。

      • 工厂方法是模板方法模式的一种特殊形式。同时,工厂方法可以作为一个大型模板方法中的一个步骤。
    • 单例模式保证一个类只有一个实例,并提供一个访问该实例的全局访问点。

    • 代理模式为另一个对象提供一个替身或占位符,从而控制对这个对象的访问。

    • 观察者模式定义对象之间的一种多对多依赖关系,当一个对象的状态法发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    • 模板模式定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

  • 在Spring框架中,还用到一些本文未提及的设计模式,例如原型模式、适配器模式、装饰器模式、责任链模式、策略模式等,有时间会继续探索

你可能感兴趣的:(java,spring,学习,设计模式)