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


最后一句话也是画外音,我自己的使用感受是:这个模式非常好的把面向过程和面向对象结合在一起,抽象业务的时候是在玩面向过程,用子类去实现的时候是在玩面向对象,如果形容的不合适,你就当看看就好。
目录
相关文章
|
29天前
|
设计模式 SQL Java
一探到底!Spring团队使用的那些设计模式,快来看看你用过哪几个
该文章主要介绍了Spring框架中使用的设计模式,并列举了一些常见的设计模式及其在Spring框架中的应用。
一探到底!Spring团队使用的那些设计模式,快来看看你用过哪几个
|
27天前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
27天前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
|
27天前
|
XML Java 数据格式
Spring5入门到实战------2、IOC容器底层原理
这篇文章深入探讨了Spring5框架中的IOC容器,包括IOC的概念、底层原理、以及BeanFactory接口和ApplicationContext接口的介绍。文章通过图解和实例代码,解释了IOC如何通过工厂模式和反射机制实现对象的创建和管理,以及如何降低代码耦合度,提高开发效率。
Spring5入门到实战------2、IOC容器底层原理
|
24天前
|
设计模式
设计模式-单一职责模式
设计模式-单一职责模式
|
24天前
|
设计模式 XML 存储
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
文章详细介绍了工厂方法模式(Factory Method Pattern),这是一种创建型设计模式,用于将对象的创建过程委托给多个工厂子类中的某一个,以实现对象创建的封装和扩展性。文章通过日志记录器的实例,展示了工厂方法模式的结构、角色、时序图、代码实现、优点、缺点以及适用环境,并探讨了如何通过配置文件和Java反射机制实现工厂的动态创建。
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
|
24天前
|
设计模式 XML Java
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
文章详细介绍了简单工厂模式(Simple Factory Pattern),这是一种创建型设计模式,用于根据输入参数的不同返回不同类的实例,而客户端不需要知道具体类名。文章通过图表类的实例,展示了简单工厂模式的结构、时序图、代码实现、优缺点以及适用环境,并提供了Java代码示例和扩展应用,如通过配置文件读取参数来实现对象的创建。
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
|
27天前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
|
27天前
|
Java Spring 容器
建模底层逻辑问题之以Spring IOC容器为例,使用因果法建模,如何操作
建模底层逻辑问题之以Spring IOC容器为例,使用因果法建模,如何操作
|
28天前
|
设计模式 uml C语言
设计模式----------工厂模式之简单工厂模式(创建型)
这篇文章详细介绍了简单工厂模式,包括其定义、应用场景、UML类图、通用代码实现、运行结果、实际应用例子,以及如何通过反射机制实现对象创建,从而提高代码的扩展性和维护性。
设计模式----------工厂模式之简单工厂模式(创建型)