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


最后一句话也是画外音,我自己的使用感受是:这个模式非常好的把面向过程和面向对象结合在一起,抽象业务的时候是在玩面向过程,用子类去实现的时候是在玩面向对象,如果形容的不合适,你就当看看就好。
目录
打赏
0
0
0
0
54
分享
相关文章
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
84 16
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
59 0
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
49 0
并发设计模式实战系列(12):不变模式(Immutable Object)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十二章,废话不多说直接开始~
45 0
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
353 26
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
并发设计模式实战系列(20):扇出/扇入模式(Fan-Out/Fan-In)(完结篇)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第二十章,废话不多说直接开始~
64 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问