深入理解Spring IOC之设计模式篇(二)、模板模式

简介: 深入理解Spring IOC之设计模式篇(二)、模板模式

今天我们要讲的这个主角是在设计模式中是个大佬级的,它就是模板模式。相信之前是有小伙伴看过这个设计模式但是确不明白它为什么这么重要的,也相信本篇会让你有新的收获。

模版模式,很多博客里面是这么说它的:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法可以不改变一个算法的结构即可重定义该算法的某些特定步骤。在我们实际编程中,通常就用是一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

我们先来看个很简单的例子,我们以生活中的做饭为例子,一般分为买东西,洗菜,最后就是烹饪这三个步骤,我们将这个过程用抽象类来表示一下就是下面的代码:


public abstract class Cook {
    protected abstract List<Food> buy();
    protected void wash(List<Food> vegetables){
        vegetables.forEach(Food::washed);
    }
    protected abstract List<CookedFood> cook(List<Food> vegetables);
    //模板方法
    public final void cook(){
        // 买东西,后代可以重写这个buy方法,也就是随便你想买啥回来都可以
        List<Food> vegetables = buy();
        // wash给了默认实现,也就是一般来说都是要洗菜的把,当然后代你可以把它重写了
        wash(vegetables);
        // 最后一步就是烹饪过程,随便你想做成什么样的食物
        List<CookedFood> cookedFoods = cook(vegetables);
    }
}


然后假如我们想吃的是鱼,那我们可以这么写:


public class CookFish extends Cook{
    @Override
    protected List<Food> buy() {
        List<Food> result = new ArrayList<>();
        // 不好意思,我饭量大,需要来只鲨鱼
        result.add(new Shark());
        result.add(new Fish());
        return result;
    }
    @Override
    protected void wash(List<Food> foods){
        System.out.println("不洗了,直接捞来的吃着更有味道");
    }
    @Override
    protected List<CookedFood> cook(List<Food> vegetables) {
        return vegetables.stream().map(CookedFood::new)
                .collect(Collectors.toList());
    }
}


其中Food是Fish和Shark的父类,就Food有一个washed的空方法,其他类都是空的,CookedFood里面有个成员属性是Food,这几个类代码过于简单,就不放出来占地方了。如果我们想做些别的饭比如龙虾什么的,那就再定义一个Cook的后代,然后自己再定义几个Food的子类,再把买、洗、烹饪这三个方法根据自己的需求重写即可。

是不是觉得很简单?好像花这么几分钟就能掌握的样子。对,就是这么简单😂,但是问题是你得玩的6才算真正的会啊,而不是仅仅是上面这一个花五分钟就能写出来的demo,这样子的话着实没什么卵用。我们一起来看看Spring中是怎么玩的吧。

我们正篇的第三篇中有提到过AbstractApplicationContext的refresh方法,很多人读spring的源码时候都是从这里开始的,这个方法其实也是个模板方法,我们来看看代码:


@Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // 1.加载前的准备工作
      prepareRefresh();
      // 2.获取一个全新的beanFactory实例
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 3.初始化beanFactory,给它设置各种
      prepareBeanFactory(beanFactory);
      try {
        // 4.允许对beanFactory中的东西做一些前置的修改,可以是增加个BeanFactoryPostProcessors这种的,也可以给增加个
        // BeanDefinition,也可以增加一些让beanFactory在自动装配时候忽略掉的接口,也可以增加一些特定场景使用的bean,
        // 比如有的后代就增加了新的scope bean 等等。但是重点是,我刚才说的这些都是基于一种具体场景的,因此这个抽象类里,
        // 这个方法是空的(不弄成抽象的原因是不强迫子类去实现)
        postProcessBeanFactory(beanFactory);
        // 5.触发调用所有的BeanFactoryPostProcessors(后边会讲这是个啥)
        invokeBeanFactoryPostProcessors(beanFactory);
        // 6.注册所有的BeanPostProcessor(后边会说这个)
        registerBeanPostProcessors(beanFactory);
        // 7.初始化支持国际化的东东
        initMessageSource();
        // 8. 初始化事件广播器
        initApplicationEventMulticaster();
        // 9. 初始化其他特殊的bean
        onRefresh();
        // 10. 注册监听器
        registerListeners();
        // 11. 实例化bean( BOSS难度代码 )
        finishBeanFactoryInitialization(beanFactory);
        // Last step: publish corresponding event.
        finishRefresh();
      }
      catch (BeansException ex) {
        logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();
        // Reset 'active' flag.
        cancelRefresh(ex);
        // Propagate exception to caller.
        throw ex;
      }
    }
  }


这里面我只说第二步,因为我们前三篇讲的其实也都只是1和2的内容,我们来看看第二处的代码:


之所以放出所有的代码只是想让大家混个脸熟,别担心,放出来的我后边都会说😊


其实第二处的代码也就两行而已,我们重点看的是第一行的refreshBeanFactory这个方法


/**
   * Tell the subclass to refresh the internal bean factory.
   * @return the fresh BeanFactory instance
   * @see #refreshBeanFactory()
   * @see #getBeanFactory()
   */
  protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
  }


注意看了哈,这是个抽象方法了,注意看这里的注释,翻译过来意思就是子类必须实现这个这个方法来进行真实的配置加载,这个方法必须在任何其他的初始化工作之前被refresh触发。我们之前看的这个方法的实现是在我们使用xml配置这种情况下对应的AbstrctApplicationContext的子类中(也就是ClassPathXmlApplicationContext的抽象父类AbstractXmlApplicationContext),为了讲清楚东西,我再带着大家回顾一下之前的代码:


@Override
  protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
    }
    try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      // 加载BeanDefinition的
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
        this.beanFactory = beanFactory;
      }
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
  }


这是AbstractRefreshAbleApplicationContext中对上面refreBeanFactory方法的实现,而这里面标着注释的这句代码loadBeanDefinitons又是一个抽象方法,而这个抽象方法的有好几个实现,一个是我们之前提到过的AbstractXmlApplicationContext中的实现,它在这个方法中完成了对xml的加载解析,而另外一个实现就是AnnotationConfigWebApplicationContext中对它的实现,在这个实现里面,完成了对把我们标着那几个常见注解的类加载成了BeanDefiniton的这个过程。

然后再回归到AbstractApplicationContext中refresh方法中的refreshBeanFactory这个抽象方法来看,不难发现,不同的子类根据不同的场景实现了这个方法,进而根据不同的配置获取到了BeanDefinition,为接下来实例化bean做足了准备工作。其实在refresh方法中,还有别的方法也有类似的功能,就不在这里一一展开,否则这又是一篇很长的文章。

我们又想想这篇文章开头那个非常简单的demo上面,确实,那个小demo简单到任何人都能理解,但是我们仔细想一想,要用好这个模板模式的前提是你必须对你的业务流程非常的了解,一个大的流程中哪些是小的流程该抽象的,哪些需要有默认的实现,而哪些又是需要给空实现,这些都是在做模板方法(其实就是在抽象你的业务流程)的时候需要考虑到的问题(模板模式看重的就是这个流程),如果在设计之初就把这个设计做好的话,模板模式会给你带来无限多扩展的好处,但是如果没有用好,以至于用了一段时间后发现模板方法有问题,那么改起来会是非常痛苦的。


最后一句话也是画外音,我自己的使用感受是:这个模式非常好的把面向过程和面向对象结合在一起,抽象业务的时候是在玩面向过程,用子类去实现的时候是在玩面向对象,如果形容的不合适,你就当看看就好。
目录
相关文章
|
17天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
19天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
12天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
31 1
|
19天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
1月前
|
Java 调度 开发者
spring的@Scheduled()有几种定时模式?
【10月更文挑战第12天】spring的@Scheduled()有几种定时模式?
71 1
|
11天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
24 0
|
1月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
21 3
|
2月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
1月前
|
设计模式 缓存 Java
面试题:谈谈Spring用到了哪些设计模式?
面试题:谈谈Spring用到了哪些设计模式?