开源框架(三) :你知道mybatis的初始化是怎么使用建造者模式创建对象的吗

都知道开源框架的优秀,有一部分是使用了设计模式, 今天我们来学习一下在mybatis的初始化中是如何使用建造者模式创建对象的.

建造者模式

首先,我们来复习一下建造者模式;

Builder 建造者模式(创建型对象类型):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示;

典型的KFC 儿童套餐:主食、辅食、饮料、玩具。这些在不同的儿童餐中可以是不同的,但是组合成儿童餐的过程是相同的。

建造者模式的类图为:
在这里插入图片描述

对应的代码:


/**
 * @ClassName: KFCChildrenMeal
 * @Description: KFC 儿童餐实体
 * @Author: 唐欢
 * @Date: 2022/6/20 10:25
 * @Version 1.0
 */
public class KFCChildrenMeal {
    private  String stapleFood;
    private String complementaryFood;
    private  String toy;
    private  String drink;

    //get和set 省略
    .....
    @Override
    public String toString() {
        return "KFCChildrenMeal{" +
                "stapleFood='" + stapleFood + '\'' +
                ", complementaryFood='" + complementaryFood + '\'' +
                ", toy='" + toy + '\'' +
                ", drink='" + drink + '\'' +
                '}';
    }
}

建造者(builder): 为了创建一个Product
对象的各个部件指定的抽象接口.


/**
 * @ClassName: Builder
 * @Description:抽象建造者/建造者接口(Builder)
 * @Author: 唐欢
 * @Date: 2022/6/20 10:22
 * @Version 1.0
 */
public interface Builder {
    /**
     * 制作主食
     */
    public   void makeStapleFood(String stapleFood);


    /**
     * 制作主食
     * @param complementaryFood
     */
    public   void makeComplementaryFood(String complementaryFood);


    /**
     * 配置玩具
     * @param toy
     */
    public  void disToy(String toy);

    /**
     * 配置书籍
     * @param drink
     */
    public  void disDrink(String drink);

    public  KFCChildrenMeal makeMeal();
}


具体建造者:实现Builder接口
构造和装配各个部件

/**
 * @ClassName: ChildrenMealBuider
 * @Description: 具体建造者  儿童餐
 * @Author: 唐欢
 * @Date: 2022/6/20 10:36
 * @Version 1.0
 */
public class ChildrenMealBuider  implements  Builder{
    KFCChildrenMeal kfcChildrenMeal = new KFCChildrenMeal();

   @Override
   public void makeStapleFood(String stapleFood) {
        kfcChildrenMeal.setStapleFood(stapleFood);
    }
    @Override
    public void makeComplementaryFood(String complementaryFood) {
        kfcChildrenMeal.setComplementaryFood(complementaryFood);
    }
    @Override
    public void disToy(String toy) {
        kfcChildrenMeal.setToy(toy);
    }
    @Override
    public void disDrink(String drink) {
        kfcChildrenMeal.setDrink(drink);
    }
    @Override
    public KFCChildrenMeal makeMeal() {
        return kfcChildrenMeal;
    }
}

指挥者:构建一个使用Builder接口的对象
它主要用于创建一个复杂的对象,

作用:
① 隔离了客户端与对象的生产过程

② 负责控制产品对象的生产过程


/**
 * @ClassName: KfcDirector
 * @Description: 指挥者
 * @Author: 唐欢
 * @Date: 2022/6/20 10:45
 * @Version 1.0
 */
public class KfcDirector {

    private  ChildrenMealBuider childrenMealBuider;

    public  void setChildrenMealBuider(ChildrenMealBuider childrenMealBuider){
        this.childrenMealBuider =childrenMealBuider;
    }

    public  KFCChildrenMeal makeKFCChildrenMeal(String stapleFood,String complementaryFood,String toy,String drink){
        childrenMealBuider.makeStapleFood(stapleFood);
        childrenMealBuider.makeComplementaryFood(complementaryFood);
        childrenMealBuider.disToy(toy);
        childrenMealBuider.disDrink(drink);
        return  childrenMealBuider.makeMeal();
    }
}

测试代码:

  @Test
    public  void builder(){

        ChildrenMealBuider childrenMealBuider = new ChildrenMealBuider();
        KfcDirector kfcDirector =new KfcDirector();
        kfcDirector.setChildrenMealBuider(childrenMealBuider);
        KFCChildrenMeal kfcChildrenMeal =kfcDirector.makeKFCChildrenMeal("汉堡","玉米","小汽车","牛奶");

        System.out.println(kfcChildrenMeal.toString());
    }

使用建造者模式的好处:

①使用建造者模式可以使客户端不必知道产品内部组成的细节。

②具体的建造者类之间是相互独立的,对系统的扩展非常有利。

③由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

mybatis 初始化

在上面我们已经对建造者模式有了一定的了解,接下来我们就来看看mybatis是如何初始化中使用建造者模式创建对象的.

mybatis 初始化的主要工作是加载并解析mybatis-config配置文件,映射配置文件以及相关的注解信息.

加载XML解析,并解析mybati-config.xml配置文件

Mybatis 的初始化入口是SqlSessionFactoryBuilder().build()方法,初始化入口代码如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6Hmmw3S-1655709129423)(77B47209479A44768D1ABB8F29130FEF)]
在这里插入图片描述
在这里插入图片描述

在重载的build()方法中创建了XMLConfigBuilder对象读取并解析mybatis-config.xml 配置文件.
在这里插入图片描述
在这里插入图片描述

MyBatis在初始化的时候,会将MyBatis的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration实例来维护.

Configuration对象的结构和xml配置文件的对象几乎相同。
mybatis-conf.xml 的配置标签有哪些:
properties (属性),settings (设置),typeAliases (类型别名),typeHandlers (类型处理
器),objectFactory (对象工厂),mappers (映射器)等;

Configuration也有对应的对象属性来封装它们
也就是说,初始化配置文件信息的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部属性中.

XML加载完成后调用XMLConfigBuilder.parse()方法进行解析.源代码如下:
在这里插入图片描述

在parse()方法中,调用parseConfiguration()方法对configuration的子节点进行解析.解析方法如下:
在这里插入图片描述

具体的节点解析方法可自行查看源码.

在mybatis 初始化时,除了加载mybatis-config.xml配置文件还,还会加载全部的映射配置文件,mybatis-config.xml配置文件中的节点会告诉mybatis去哪里查找映射文件以及使用了配置注解表示的接口. 节点定义:

<!--映射器:指定映射文件或映射类 必写-->
    <mappers>
        <!-- 方式一:使用相对于类路径的资源引用,开发中常用方式 -->
        <mapper resource="mapper/UserMapper.xml"/>
        <!-- 方式二:使用完全限定资源定位符(URL) -->
        <mapper url="file:///mapper/UserMapper.xml"/>
        <!-- 方式三:使用映射器接口实现类的完全限定类名 -->
        <mapper class="org.mybatis.builder.AuthorMapper"/>
        <!-- 方式四:将包内的映射器接口实现全部注册为映射器 -->
        <package name="org.mybatis.builder"/>
    </mappers>

XMLConfigBuilder.mapperElement() 方法是主要负责解析节点,它会创建XMLMapperBuilder 对象加载映射文件,如果映射配置文件存在相应的mapper接口,也会加载相应的Mapper接口,解析其中的注解并完成向MapperRegistry的注册,源码如下:

在这里插入图片描述

解析映射文件

XMLMapperBuilder 负责解析映射配置文件,其中XMLMapperBuilder.parse()方法是解析映射文件的入口,具体代码如下:
在这里插入图片描述

XMLMapperBuilder同XMLConfigBuilder 一样,也是将每个节点的解析过程封装成一个方法.XMLMapperBuilder.configurationElement() 方法进行解析,解析的源代码如下:
在这里插入图片描述

每个节点具体的解析方式可自行查看源代码.

解析定义的SQL语句

在mapper映射文件中,SQL节点主要定义SQL语句,SQL语句主要由XMLStatementBuilder 负责进行解析XMLStatementBuilder.parseStatementNode()方法是解析SQL节点的入口函数,源代码如下:
在这里插入图片描述

映射配置文件与对应的mapper接口绑定

每个映射配置文件的命名空间可以绑定一个Mapper接口,并注册到MapperRegister中.在XMLMapperBuilder.bindMapperForNamespace()方法职工,完成映射配置文件与对应Mapper 接口的绑定,具体实现如下:
在这里插入图片描述

处理异常节点结合incomplete*集合

XMLMapperBuilder.configurationElement()方法解析映射配置文件时,是按照从文件头到文件尾的顺序解析的,但是有时候在解析一个节点时,会引用定义在该节点之后和还未解析的节点,就会导致解析失败并抛出BuilderException. 根据抛出异常的节点不同,Mabatis 会创建不同的Resolver对象.并添加到Configuration的不同incomplete集合中.

通过configurationElement() 方法完成一次映射配置文件后,会调用parsePendingResultMaps()、parsePendingCacheRefs()、parsePendingStatements()方法处理configuration中对应的三个incomplete*集合。以parsePendingStatements() 方法为例进行分析,具体实现如下:
在这里插入图片描述

到目前为止,mybatis的初始化过程就完成了。

初始中的建造者模式

mybatis初始化的类都集成了一个抽象类BaseBuilder,这个抽象类对常用的方法进行封装了。继承它的类图如下:
在这里插入图片描述

以XMLMapperBuilder为例来看看在初始化过程中的建造者模式,
在这里插入图片描述

BaseBuilder 抽象类在这扮演这建造接口的角色,
其中 XMLMapperBuilder 、XMLConfigBuilder、XMLStatementBuilder 都是具体建造者角色。但是XMLMapperBuilder 、XMLConfigBuilder、XMLStatementBuilder同时也扮演着Director角色。

在mybatis中,除了mybatis初始化使用了建造者模式,在cache 也是用了建造者模式。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

弯_弯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值