• 开源框架(三) :你知道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 + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    建造者(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();
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    具体建造者:实现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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    指挥者:构建一个使用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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试代码:

      @Test
        public  void builder(){
    
            ChildrenMealBuider childrenMealBuider = new ChildrenMealBuider();
            KfcDirector kfcDirector =new KfcDirector();
            kfcDirector.setChildrenMealBuider(childrenMealBuider);
            KFCChildrenMeal kfcChildrenMeal =kfcDirector.makeKFCChildrenMeal("汉堡","玉米","小汽车","牛奶");
    
            System.out.println(kfcChildrenMeal.toString());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用建造者模式的好处:

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

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

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

    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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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
    • 3
    • 4
  • 相关阅读:
    阿里Java研发面经(已拿offer)
    美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?
    我开始学习golang编程语言-1
    图论------如何使用矩阵来存储图的信息(邻接矩阵表示法)。
    CocosCreator3.8研究笔记(十四)CocosCreator 资源管理Asset Manager
    解决 UDP 接收不到数据问题
    android MQTT使用
    ChatGPT如何协助人们学习新的科学和技术概念?
    python基础学习笔记1
    11. 机器学习 - 评价指标2
  • 原文地址:https://blog.csdn.net/tang_huan_11/article/details/125373339