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閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容

  • 1 Mybatis入門 1.1 單獨使用jdbc編程問題總結 1.1.1 jdbc程序 上邊使...
    哇哈哈E閱讀 3,320評論 0 38
  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的...
    笨鳥慢飛閱讀 5,571評論 0 4
  • 今日突然想起一件小時候莫名其妙的事情。 大概也是小學二年級左右,一次放學回家,和幾個要好的同學在路口等紅綠燈。此時...
    黑彩閱讀 299評論 2 1
  • 要考試了,想睡覺。祝考試順利。
    蝸牛在仰泳閱讀 292評論 0 0
  • 鳳冠霞帔,落遮美人顏。 中國傳統婚禮,一生一次,紅色的秀禾服從古穿到今。爆竹聲落,嗩吶音起,高頭大馬,八臺大轎,這...
    潘朵拉魔女閱讀 472評論 0 2