mybatis核心层源码分析1-配置加载阶段

1.建造者模式

Builder Pattern使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。


  • Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建;
    ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。在建造过程完成后,提供产品的实例;
    Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建;
    Product:要创建的复杂对象。

建造者模式使用场景:

  • 需要生成的对象具有复杂的内部结构,实例化对象时要屏蔽掉对象内部细节,让上层代码与复杂对象的实例化过程解耦,可以使用建造者模式。简而言之,如果遇到多个构造器参数时要考虑用建造者模式。
  • 一个对象的实例化是依赖各个组件的产生以及装配顺序,关注的是一步一步地组装出目标对象,可以使用建造者模式。

与工厂模式区别:

  • 对象复杂度
    建造者建造的对象更加负责,是一个复合产品,它由各个部件复合而成,部件不同产品对象不同,生成的产品粒度细;
    在工厂方法模式里,关注的是一个产品整体,无须关心产品的各部分是如何创建出来的。
  • 客户端参与程度
    建造者模式,导演对象参与了产品的创建,决定了产品的类型和内容,参与度高,适合实例化对象时属性变化频繁的场景;
    工厂模式,客户端对产品的创建过程参与度低,对象实例化时属性值相对比较固定。

2.建造者模式实例

建造者模式参考代码

2.1 Builder抽象接口

public interface RedPacketBuilder {

    RedPacketBuilder setPublisherName(String publishName);

    RedPacketBuilder setAcceptName(String acceptName);

    RedPacketBuilder setPacketAmount(BigDecimal packetAmount);

    RedPacketBuilder setPacketType(int packetType);

    RedPacketBuilder setPulishPacketTime(Date pushlishPacketTime);

    RedPacketBuilder setOpenPacketTime(Date openPacketTime);

    RedPacket build();
}

2.2 实际的Builder

注意点:
1)RedPacketBuilderImpl包含RedPacket的所有域
2)set方法设置一个域,并且返回this,这是流式编程的关键
3)最后build调用一个完整包含所有域的RedPacket构造函数

public class RedPacketBuilderImpl implements RedPacketBuilder {

    private String publisherName;

    private String acceptName;

    private BigDecimal packetAmount;

    private int packetType;

    private Date pulishPacketTime;

    private Date openPacketTime;

    public static RedPacketBuilderImpl getBulider(){
        return new RedPacketBuilderImpl();
    }


    @Override
    public RedPacketBuilder setPublisherName(String publishName) {
        this.publisherName = publishName;
        return this;
    }

    @Override
    public RedPacketBuilder setAcceptName(String acceptName) {
        this.acceptName = acceptName;
        return this;
    }

    @Override
    public RedPacketBuilder setPacketAmount(BigDecimal packetAmount) {
        this.packetAmount = packetAmount;
        return this;
    }

    @Override
    public RedPacketBuilder setPacketType(int packetType) {
        this.packetType = packetType;
        return this;
    }

    @Override
    public RedPacketBuilder setPulishPacketTime(Date pushlishPacketTime) {
        this.pulishPacketTime = pushlishPacketTime;
        return this;
    }

    @Override
    public RedPacketBuilder setOpenPacketTime(Date openPacketTime) {
        this.openPacketTime = openPacketTime;
        return this;
    }


    public RedPacket build() {
        return new RedPacket(publisherName,acceptName,packetAmount,packetType,pulishPacketTime,openPacketTime);
    }
}

2.3 具体的对象类

public class RedPacket {

    private String publisherName; //发包人

    private String acceptName; //手包人

    private BigDecimal packetAmount; //红包金额

    private int packetType; //红包类型

    private Date pulishPacketTime; //发包时间

    private Date openPacketTime; //抢包时间

    public RedPacket(String publisherName, String acceptName, BigDecimal packetAmount, int packetType, Date pulishPacketTime, Date openPacketTime) {
        this.publisherName = publisherName;
        this.acceptName = acceptName;
        this.packetAmount = packetAmount;
        this.packetType = packetType;
        this.pulishPacketTime = pulishPacketTime;
        this.openPacketTime = openPacketTime;
    }

    public String getPublisherName() {
        return publisherName;
    }

    public void setPublisherName(String publisherName) {
        this.publisherName = publisherName;
    }

    public String getAcceptName() {
        return acceptName;
    }

    public void setAcceptName(String acceptName) {
        this.acceptName = acceptName;
    }

    public BigDecimal getPacketAmount() {
        return packetAmount;
    }

    public void setPacketAmount(BigDecimal packetAmount) {
        this.packetAmount = packetAmount;
    }

    public int getPacketType() {
        return packetType;
    }

    public void setPacketType(int packetType) {
        this.packetType = packetType;
    }

    public Date getPulishPacketTime() {
        return pulishPacketTime;
    }

    public void setPulishPacketTime(Date pulishPacketTime) {
        this.pulishPacketTime = pulishPacketTime;
    }

    public Date getOpenPacketTime() {
        return openPacketTime;
    }

    public void setOpenPacketTime(Date openPacketTime) {
        this.openPacketTime = openPacketTime;
    }

    @Override
    public String toString() {
        return "RedPacket [publisherName=" + publisherName + ", acceptName="
                + acceptName + ", packetAmount=" + packetAmount
                + ", packetType=" + packetType + ", pulishPacketTime="
                + pulishPacketTime + ", openPacketTime=" + openPacketTime + "]";
    }

}

2.4 导演

public class Director {

    public static void main(String[] args) {
        RedPacket redPacket = RedPacketBuilderImpl.getBulider().setPublisherName("lison")
                .setAcceptName("vip群")
                .setPacketAmount(new BigDecimal("888"))
                .setPacketType(1)
                .setOpenPacketTime(new Date())
                .setPulishPacketTime(new Date()).build();

        System.out.println(redPacket);
    }
}

结果:

RedPacket [publisherName=lison, acceptName=vip群, packetAmount=888, packetType=1, pulishPacketTime=Thu Sep 27 16:44:44 CST 2018, openPacketTime=Thu Sep 27 16:44:44 CST 2018]

3.MyBatis的初始化

  • XMLConfigBuilder:主要负责解释mybatis-config.xml
    XMLMapperBuilder:负责解析映射配置文件
    XMLStatementBuilder:负责解析映射配置文件中的SQL结点

3.1 映射器的关键类

  • Configuration:Mybatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象中,Configuration是单例的,生命周期是应用级的
  • MapperRegistry:mapper接口动态代理工厂类的注册中心。在Mybatis中,通过mapperProxy实现InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象
  • ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id、result等子元素
  • MappedStatement:用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点很多重要属性
  • SqlSource:mapper.xml文件中sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接交给数据库执行。

3.2 Configuration的域对应mybatis-config.xml中相应的配置项

整体配置

setting部分配置
public class Configuration {

  protected Environment environment;

  /* 是否启用数据组A_column自动映射到Java类中的驼峰命名的属性**/
  protected boolean mapUnderscoreToCamelCase;
  
  /*当对象使用延迟加载时 属性的加载取决于能被引用到的那些延迟属性,否则,按需加载(需要的是时候才去加载)**/
  protected boolean aggressiveLazyLoading;
  
  /*是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true **/
  protected boolean multipleResultSetsEnabled = true;
  
  /*-允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false**/
  protected boolean useGeneratedKeys;
   
  /*配置全局性的cache开关,默认为true**/
  protected boolean cacheEnabled = true;

  /*指定 MyBatis 应如何自动映射列到字段或属性*/
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;

  /*MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO*/
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

  /*延迟加载的全局开关*/
  protected boolean lazyLoadingEnabled = false;
  
  /*指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具*/
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  /*插件集合*/
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  
  /*TypeHandler注册中心*/
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  
  /*TypeAlias注册中心*/
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
  //-------------------------------------------------------------

  /*mapper接口的动态代理注册中心*/
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

  /*mapper文件中增删改查操作的注册中心*/
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
  
  /*mapper文件中配置的所有resultMap对象  key为命名空间+ID*/
  protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
  
  /*加载到的所有*mapper.xml文件*/
  protected final Set<String> loadedResources = new HashSet<>();
  

3.3 XMLConfigBuilder解析mybatis-config.xml,将解析出的相关的值加入到Configuration对象中

    @Before
    public void init() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 1.读取mybatis配置文件创SqlSessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        inputStream.close();
    }
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

具体的解析方法:

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
     //解析<properties>节点
      propertiesElement(root.evalNode("properties"));
      //解析<settings>节点
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      //解析<typeAliases>节点
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析<plugins>节点
      pluginElement(root.evalNode("plugins"));
      //解析<objectFactory>节点
      objectFactoryElement(root.evalNode("objectFactory"));
      //解析<objectWrapperFactory>节点
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //解析<reflectorFactory>节点
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);//将settings填充到configuration
      // read it after objectFactory and objectWrapperFactory issue #631
      //解析<environments>节点
      environmentsElement(root.evalNode("environments"));
      //解析<databaseIdProvider>节点
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析<typeHandlers>节点
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析<mappers>节点
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
整体配置

3.4 XMLMapperBuilder解析mapper.xml映射文件

    <!-- 映射文件,mapper的配置文件 -->
    <mappers>
        <!--直接映射到相应的mapper文件 -->
        <mapper resource="sqlmapper/TUserMapper.xml" />
        <mapper resource="sqlmapper/TJobHistoryMapper.xml" />
        <mapper resource="sqlmapper/TPositionMapper.xml" />
        <mapper resource="sqlmapper/THealthReportFemaleMapper.xml" />
        <mapper resource="sqlmapper/THealthReportMaleMapper.xml" />
         <mapper class="com.enjoylearning.mybatis.mapper.TJobHistoryAnnoMapper"/>
    </mappers>

解析方法:

      //解析<mappers>节点
      mapperElement(root.evalNode("mappers"));

XMLMapperBuilder解析mapper映射文件:

  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {//处理mapper子节点
        if ("package".equals(child.getName())) {//package子节点
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {//获取<mapper>节点的resource、url或mClass属性这三个属性互斥
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {//如果resource不为空
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);//加载mapper文件
            //实例化XMLMapperBuilder解析mapper映射文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {//如果url不为空
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);//加载mapper文件
            //实例化XMLMapperBuilder解析mapper映射文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {//如果class不为空
            Class<?> mapperInterface = Resources.classForName(mapperClass);//加载class对象
            configuration.addMapper(mapperInterface);//向代理中心注册mapper
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
  public void parse() {
    //判断是否已经加载该配置文件
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));//处理mapper节点
      configuration.addLoadedResource(resource);//将mapper文件添加到configuration.loadedResources中
      bindMapperForNamespace();//注册mapper接口
    }
    //处理解析失败的ResultMap节点
    parsePendingResultMaps();
    //处理解析失败的CacheRef节点
    parsePendingCacheRefs();
    //处理解析失败的Sql语句节点
    parsePendingStatements();
  }
private void configurationElement(XNode context) {
    try {
        //获取mapper节点的namespace属性
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //设置builderAssistant的namespace属性
      builderAssistant.setCurrentNamespace(namespace);
      //解析cache-ref节点
      cacheRefElement(context.evalNode("cache-ref"));
      //重点分析 :解析cache节点----------------1-------------------
      cacheElement(context.evalNode("cache"));
      //解析parameterMap节点(已废弃)
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //重点分析 :解析resultMap节点(基于数据结果去理解)----------------2-------------------
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql节点
      sqlElement(context.evalNodes("/mapper/sql"));
      //重点分析 :解析select、insert、update、delete节点 ----------------3-------------------
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

解析mapper.xml文件,例如TUserMapper.xml:

<mapper namespace="com.enjoylearning.mybatis.mapper.TUserMapper">

    <cache></cache>

    <resultMap id="BaseResultMap" type="TUser">

        <!-- <constructor> <idArg column="id" javaType="int"/> <arg column="user_name"
            javaType="String"/> </constructor> -->
        <id column="id" property="id" />
        <result column="user_name" property="userName" />
        <result column="real_name" property="realName" />
        <result column="sex" property="sex" />
        <result column="mobile" property="mobile" />
        <result column="email" property="email" />
        <result column="note" property="note" />
    </resultMap>




    <resultMap id="userAndPosition1" extends="BaseResultMap" type="TUser">
        <association property="position" javaType="TPosition" columnPrefix="post_">
            <id column="id" property="id"/>
            <result column="name" property="postName"/>
            <result column="note" property="note"/>
        </association>
    </resultMap>

    <resultMap id="userAndPosition2" extends="BaseResultMap" type="TUser">
        <!--<association property="position"  column="position_id" select="com.enjoylearning.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />-->
        <association property="position" fetchType="lazy"  column="position_id" select="com.enjoylearning.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />
    </resultMap>



    <select id="selectUserPosition1" resultMap="userAndPosition1">
        select
            a.id,
            user_name,
            real_name,
            sex,
            mobile,
            email,
            a.note,
            b.id  post_id,
            b.post_name,
            b.note post_note
        from t_user a,
            t_position b
        where a.position_id = b.id

    </select>

3.4.1 解析缓存节点cache

      //重点分析 :解析cache节点----------------1-------------------
      cacheElement(context.evalNode("cache"));
  private void cacheElement(XNode context) throws Exception {
    if (context != null) {
      //获取cache节点的type属性,默认为PERPETUAL
      String type = context.getStringAttribute("type", "PERPETUAL");
      //找到type对应的cache接口的实现
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      //读取eviction属性,既缓存的淘汰策略,默认LRU
      String eviction = context.getStringAttribute("eviction", "LRU");
      //根据eviction属性,找到装饰器
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      //读取flushInterval属性,既缓存的刷新周期
      Long flushInterval = context.getLongAttribute("flushInterval");
      //读取size属性,既缓存的容量大小
      Integer size = context.getIntAttribute("size");
     //读取readOnly属性,既缓存的是否只读
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      //读取blocking属性,既缓存的是否阻塞
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      //通过builderAssistant创建缓存对象,并添加至configuration
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }
 //通过builderAssistant创建缓存对象,并添加至configuration
  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
    //经典的建造起模式,创建一个cache对象
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    //将缓存添加至configuration,注意二级缓存以命名空间为单位进行划分
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }

3.4.2 解析resultMap(整体思路都是一样的)

      //重点分析 :解析resultMap节点(基于数据结果去理解)----------------2-------------------
      resultMapElements(context.evalNodes("/mapper/resultMap"));
  //解析resultMap节点,实际就是解析sql查询的字段与pojo属性之间的转化规则
  private void resultMapElements(List<XNode> list) throws Exception {
    //遍历所有的resultmap节点
    for (XNode resultMapNode : list) {
      try {
         //解析具体某一个resultMap节点
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
  }

用来存储resultMap节点的ResultMap对象:


public class Configuration {
  /*mapper文件中配置的所有resultMap对象  key为命名空间+ID*/
  protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
public class ResultMap {
  private Configuration configuration;//configuration对象

  private String id;//resultMap的id属性
  private Class<?> type;//resultMap的type属性
  private List<ResultMapping> resultMappings;//除discriminator节点之外的映射关系
  private List<ResultMapping> idResultMappings;//记录ID或者<constructor>中idArg的映射关系
  private List<ResultMapping> constructorResultMappings;////记录<constructor>标志的映射关系
  private List<ResultMapping> propertyResultMappings;//记录非<constructor>标志的映射关系
  private Set<String> mappedColumns;//记录所有有映射关系的columns字段
  private Set<String> mappedProperties;//记录所有有映射关系的property字段
  private Discriminator discriminator;//鉴别器,对应discriminator节点
  private boolean hasNestedResultMaps;//是否有嵌套结果映射
  private boolean hasNestedQueries;////是否有嵌套查询
  private Boolean autoMapping;//是否开启了自动映射
public class ResultMapping {

  private Configuration configuration;//引用的configuration对象
  private String property;//对应节点的property属性
  private String column;//对应节点的column属性
  private Class<?> javaType;//对应节点的javaType属性
  private JdbcType jdbcType;//对应节点的jdbcType属性
  private TypeHandler<?> typeHandler;//对应节点的typeHandler属性
  private String nestedResultMapId;////对应节点的resultMap属性,嵌套结果时使用
  private String nestedQueryId;////对应节点的select属性,嵌套查询时使用
  private Set<String> notNullColumns;//对应节点的notNullColumn属性
  private String columnPrefix;//对应节点的columnPrefix属性
  private List<ResultFlag> flags;//标志,id 或者 constructor
  private List<ResultMapping> composites;
  private String resultSet;//对应节点的resultSet属性
  private String foreignColumn;//对应节点的foreignColumn属性
  private boolean lazy;//对应节点的fetchType属性,是否延迟加载

3.4.3 XMLStatementBuilder解析SQL语句

      //重点分析 :解析select、insert、update、delete节点 ----------------3-------------------
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  //解析select、insert、update、delete节点
  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }
  //处理所有的sql语句节点并注册至configuration对象
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      //创建XMLStatementBuilder 专门用于解析sql语句节点
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        //解析sql语句节点
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

增删改查sql语句对应的对象MappedStatement:


4.Configuration建造过程的总结

4.1 建造者模式的灵魂

      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
            //实例化XMLMapperBuilder解析mapper映射文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();

XMLConfigBuilder,XMLMapperBuilder和XMLStatementBuilder整体层次上并没有采用建造者的流式编程风格,但是采用了建造者模式的灵魂,因为这三个建造者分别构建了Configuration对象的不同部分。

4.2 MapperBuilderAssistant builderAssistant调用经典的建造者模式

 //通过builderAssistant创建缓存对象,并添加至configuration
  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
    //经典的建造起模式,创建一个cache对象
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    //将缓存添加至configuration,注意二级缓存以命名空间为单位进行划分
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
  }

经典建造者模式CacheBuilder:

public class CacheBuilder {

  public Cache build() {
      //设置缓存的主实现类为PerpetualCache
    setDefaultImplementations();
    //通过反射实例化PerpetualCache对象
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);//根据cache节点下的<property>信息,初始化cache
    // issue #352, do not apply decorators to custom caches
    
    if (PerpetualCache.class.equals(cache.getClass())) {//如果cache是PerpetualCache的实现,则为其添加标准的装饰器
      for (Class<? extends Cache> decorator : decorators) {//为cache对象添加装饰器,这里主要处理缓存清空策略的装饰器
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      //通过一些属性为cache对象添加装饰器
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      //如果cache不是PerpetualCache的实现,则为其添加日志的能力
      cache = new LoggingCache(cache);
    }
    return cache;
  }

4.3 各个节点的构造思路

  • step1.解析XML节点的值
  • step2.通过MapperBuilder调用相应节点对象建造者
  • step3.加入到Configuration对象中

4.4 源码分析的核心思路

  • step1.搞清楚XML各节点解析出来的对象类结构
    也即XML结点与相关类的对应关系
  • step2.知道了源点(XML节点)和目标(对象类),然后找到从源点到目标对象之间调用的方法,搞清楚方法是怎么建造的即可

参考

  • 1)享学课堂Lison老师笔记
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 1 Mybatis入门 1.1 单独使用jdbc编程问题总结 1.1.1 jdbc程序 上边使...
    哇哈哈E阅读 3,291评论 0 38
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,423评论 0 4
  • 今日突然想起一件小时候莫名其妙的事情。 大概也是小学二年级左右,一次放学回家,和几个要好的同学在路口等红绿灯。此时...
    黑彩阅读 277评论 2 1
  • 要考试了,想睡觉。祝考试顺利。
    蜗牛在仰泳阅读 211评论 0 0
  • 凤冠霞帔,落遮美人颜。 中国传统婚礼,一生一次,红色的秀禾服从古穿到今。爆竹声落,唢呐音起,高头大马,八台大轿,这...
    潘朵拉魔女阅读 434评论 0 2