第三章 MyBatis源码分析笔记1

一、MyBatis核心流程分析

mybatis核心流程三大阶段

初始化阶段:读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作。

代理阶段:封装iBatis的编程模型,使用mapper接口开发的初始化工作。

数据读写阶段:通过SqlSession完成SQL的解析,参数的映射、SQL的执行、结果的解析过程。

二、配置加载阶段

1、Mybatis的初始化建造者模式

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

Product:要创建的复杂对象

Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建;

ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例;

Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建;

第三章 MyBatis源码分析笔记1_第1张图片

 

1.1、建造者模式使用场景

需要生成的对象具有复杂的内部结构,实例化对象时要屏蔽掉对象内部的细节,让上层代码与复杂对象的实例化过程解耦,可以使用建造者模式;简而言之,如果“遇到多个构造器参数时要考虑用构建器”;

对象的实例化是依赖各个组件的产生以及装配顺序,关注的是一步一步地组装出目标对象,可以使用建造器模式;

建造者模式与工厂模式之间的区别:

第三章 MyBatis源码分析笔记1_第2张图片

 

1.2、建造者模式—示例代码

1)建造者实体类:

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;
    }

    getXXX() setXXX() ....

2)建造者接口

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();
}

3)建造者实现类

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);
    }
}

4)建造者示例:

public class Director {
   public static void main(String[] args) {
      RedPacket redPacket = RedPacketBuilderImpl.getBulider().setPublisherName("hankin")

.setAcceptName("chj").setPacketAmount(new BigDecimal("888")).setPacketType(1)

.setOpenPacketTime(new Date()).setPulishPacketTime(new Date()).build();
      System.out.println(redPacket);
   }
}

2、Mybatis的初始化

第三章 MyBatis源码分析笔记1_第3张图片

 

代码示例:

private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
   //--------------------第一阶段---------------------------
    // 1.读取mybatis配置文件创SqlSessionFactory
   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, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());
  }
}

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

配置解析入口:mybatis-config.xml配置文件解析过程

private void parseConfiguration(XNode root) {
  try {//issue #117 read properties first
   //解析节点
    propertiesElement(root.evalNode("properties"));
    //解析节点
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    //解析节点
    typeAliasesElement(root.evalNode("typeAliases"));
    //解析节点
    pluginElement(root.evalNode("plugins"));
    //解析节点
    objectFactoryElement(root.evalNode("objectFactory"));
    //解析节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    //解析节点
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);//将settings填充到configuration
    // read it after objectFactory and objectWrapperFactory issue #631
    // 解析节点
    environmentsElement(root.evalNode("environments"));
    //解析节点
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    //解析节点
    typeHandlerElement(root.evalNode("typeHandlers"));
    //解析节点
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

配置文件中的各个配置属性对应里面的各个方法,也对应着具体每个解析类,这里我们重点分析mapperElement(root.evalNode("mappers"))方法,代码如下:

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 {//获取节点的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.");
        }
      }
    }
  }
}

加载到的所有*mapper.xml文件

protected final Set loadedResources = new HashSet<>();

//注册mapper接口
  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();//获取命名空间
    if (namespace != null) {
      Class boundType = null;
      try {
       //通过命名空间获取mapper接口的class对象
        boundType = Resources.classForName(namespace);
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {//是否已经注册过该mapper接口?
          //将命名空间添加至configuration.loadedResource集合中
          configuration.addLoadedResource("namespace:" + namespace);
          //将mapper接口添加到mapper注册中心
          configuration.addMapper(boundType);
        }
      }
    }
  }

public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

//将mapper接口的工厂类添加到mapper注册中心
public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
        if (hasMapper(type)) {
          throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
        }
      boolean loadCompleted = false;
      try {
       //实例化Mapper接口的代理工程类,并将信息添加至knownMappers
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        //解析接口上的注解信息,并添加至configuration对象
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

//记录了mapper接口与对应MapperProxyFactory之间的关系
private final Map, MapperProxyFactory> knownMappers = new HashMap<>();

3、MyBatis建造者类图

第三章 MyBatis源码分析笔记1_第4张图片

 

XMLConfigBuilder: 主要负责解析mybatis-config.xml;

XMLMapperBuilder: 主要负责解析映射配置文件;

XMLStatementBuilder: 主要负责解析映射配置文件中的SQL节点

TIPS:MyBatis的初始化过程把建造者模式演绎成了工厂模式;

//实例化XMLMapperBuilder解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();

private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map sqlFragments) {
  super(configuration);
  this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
  this.parser = parser;
  this.sqlFragments = sqlFragments;
  this.resource = resource;
}

调用解析方法:

public void parse() {//判断是否已经加载该配置文件
   if (!configuration.isResourceLoaded(resource)) {
     configurationElement(parser.evalNode("/mapper"));//处理mapper节点
     configuration.addLoadedResource(resource);//将mapper文件添加到configuration.loadedResources中
     bindMapperForNamespace();//注册mapper接口
   }
   parsePendingResultMaps();//处理解析失败的ResultMap节点
   parsePendingCacheRefs();//处理解析失败的CacheRef节点
   parsePendingStatements();//处理解析失败的Sql语句节点
 }

处理mapper节点:

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.setCurrentNamespace(namespace); //设置builderAssistant的namespace属性
    cacheRefElement(context.evalNode("cache-ref"));//解析cache-ref节点
    //重点分析1 :解析cache节点----------------1-------------------
    cacheElement(context.evalNode("cache"));
    //解析parameterMap节点(已废弃)
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //重点分析 2:解析resultMap节点(基于数据结果去理解)----------------2-------------------
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));//解析sql节点
    //重点分析 3:解析select、insert、update、delete节点 ----------------3-------------------
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  }
}

重点1: 解析cache节点,解析配置的缓存策略等等信息

private void cacheElement(XNode context) throws Exception {
  if (context != null) {
    //获取cache节点的type属性,默认为PERPETUAL
    String type = context.getStringAttribute("type", "PERPETUAL");
    //找到type对应的cache接口的实现
    Classextends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    //读取eviction属性,既缓存的淘汰策略,默认LRU
    String eviction = context.getStringAttribute("eviction", "LRU");
    //根据eviction属性,找到装饰器
    Classextends 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(Classextends Cache> typeClass,Classextends 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;
 }

public void addCache(Cache cache) {
  caches.put(cache.getId(), cache);
}

/*mapper文件中配置cache节点的 二级缓存*/
protected final Map caches = new StrictMap<>("Caches collection");

重点2: 解析resultMap节点

resultMapElements(context.evalNodes("/mapper/resultMap"));

解析resultMap节点,实际就是解析sql查询的字段与pojo属性之间的转化规则。

例如如下配置代码的解析过程:

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

  <id column="id" property="id" jdbcType="INTEGER" />
  <result column="user_name" property="userName" jdbcType="VARCHAR" />
  <result column="real_name" property="realName" jdbcType="VARCHAR" />
  <result column="sex" property="sex" jdbcType="TINYINT" />
  <result column="mobile" property="mobile" jdbcType="VARCHAR" />
  <result column="email" property="email" jdbcType="VARCHAR" />
  <result column="note" property="note" jdbcType="VARCHAR" />
  
  
  <association property="position"  column="position_id" select="com.chj.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />
resultMap>

对应的代码逻辑:

//解析resultMap节点,实际就是解析sql查询的字段与pojo属性之间的转化规则
 private void resultMapElements(List list) throws Exception {
//遍历所有的resultmap节点
   for (XNode resultMapNode : list) {
     try { //解析具体某一个resultMap节点
       resultMapElement(resultMapNode);
     }
   }
 }

private ResultMap resultMapElement(XNode resultMapNode, List additionalResultMappings) throws Exception {
  ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
  //获取resultmap节点的id属性
  String id = resultMapNode.getStringAttribute("id",resultMapNode.getValueBasedIdentifier());
  //获取resultmap节点的type属性
  String type = resultMapNode.getStringAttribute("type",resultMapNode.getStringAttribute("ofType",
          resultMapNode.getStringAttribute("resultType",resultMapNode.getStringAttribute("javaType"))));
  //获取resultmap节点的extends属性,描述继承关系
  String extend = resultMapNode.getStringAttribute("extends");
  //获取resultmap节点的autoMapping属性,是否开启自动映射
  Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
  //从别名注册中心获取entity的class对象
  Class typeClass = resolveClass(type);
  Discriminator discriminator = null;
  //记录子节点中的映射结果集合
  List resultMappings = new ArrayList<>();
  resultMappings.addAll(additionalResultMappings);
  //从xml文件中获取当前resultmap中的所有子节点,并开始遍历
  List resultChildren = resultMapNode.getChildren();
  for (XNode resultChild : resultChildren) {
    if ("constructor".equals(resultChild.getName())) {//处理节点
      processConstructorElement(resultChild, typeClass, resultMappings);
    } else if ("discriminator".equals(resultChild.getName())) {//处理节点
      discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
    } else {//处理 节点
      List flags = new ArrayList<>();

//
      if ("id".equals(resultChild.getName())) {
        flags.add(ResultFlag.ID);//如果是id节点,向flags中添加元素
      }
      //创建ResultMapping对象并加入resultMappings集合中
      resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
    }
  }
  //实例化resultMap解析器
  ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  try {
    //通过resultMap解析器实例化resultMap并将其注册到configuration对象
    return resultMapResolver.resolve();
  } catch (IncompleteElementException  e) {
    configuration.addIncompleteResultMap(resultMapResolver);
    throw e;
  }
}

根据resultmap中的子节点信息,创建resultMapping对象:

select="com.chj.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />

//根据resultmap中的子节点信息,创建resultMapping对象
private ResultMapping buildResultMappingFromContext(XNode context, Class resultType, List flags) throws Exception {
  String property;
  if (flags.contains(ResultFlag.CONSTRUCTOR)) {
    property = context.getStringAttribute("name");
  } else {
    property = context.getStringAttribute("property");
  }
  String column = context.getStringAttribute("column");
  String javaType = context.getStringAttribute("javaType");
  String jdbcType = context.getStringAttribute("jdbcType");
  String nestedSelect = context.getStringAttribute("select");
  String nestedResultMap = context.getStringAttribute("resultMap",
      processNestedResultMappings(context, Collections. emptyList()));
  String notNullColumn = context.getStringAttribute("notNullColumn");
  String columnPrefix = context.getStringAttribute("columnPrefix");
  String typeHandler = context.getStringAttribute("typeHandler");
  String resultSet = context.getStringAttribute("resultSet");
  String foreignColumn = context.getStringAttribute("foreignColumn");
  boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
  Class javaTypeClass = resolveClass(javaType);
  @SuppressWarnings("unchecked")
  Classextends TypeHandler> typeHandlerClass = (Classextends TypeHandler>) resolveClass(typeHandler);
  JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
  //使用建造者模式创建resultMapping对象
  return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}

通过resultMap解析器实例化resultMap并将其注册到configuration对象(MapperBuilderAssistant)

public ResultMap resolve() {
  return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}

//实例化resultMap并将其注册到configuration对象
public ResultMap addResultMap( String id, Class type, String extend,
    Discriminator discriminator,List resultMappings,Boolean autoMapping) {
//完善id,id的完整格式是"namespace.id"
  id = applyCurrentNamespace(id, false);
  //获得父类resultMap的完整id
  extend = applyCurrentNamespace(extend, true);
  //针对extend属性的处理
  if (extend != null) {
    if (!configuration.hasResultMap(extend)) {
      throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
    }
    ResultMap resultMap = configuration.getResultMap(extend);
    List extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
    extendedResultMappings.removeAll(resultMappings);
    // Remove parent constructor if this resultMap declares a constructor.
    boolean declaresConstructor = false;
    for (ResultMapping resultMapping : resultMappings) {
      if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
        declaresConstructor = true;
        break;
      }
    }
    if (declaresConstructor) {
      Iterator extendedResultMappingsIter = extendedResultMappings.iterator();
      while (extendedResultMappingsIter.hasNext()) {
        if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          extendedResultMappingsIter.remove();
        }
      }
    }
    //添加需要被继承下来的resultMapping对象结合
    resultMappings.addAll(extendedResultMappings);
  }
  //通过建造者模式实例化resultMap,并注册到configuration.resultMaps中
  ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
      .discriminator(discriminator)
      .build();
  configuration.addResultMap(resultMap);
  return resultMap;
}

mapper文件中配置的所有resultMap对象key为命名空间+ID

protected final Map resultMaps = new StrictMap<>("Result Maps collection");

//实例化resultMap并将其注册到configuration对象
public ResultMap addResultMap( String id,Class type,String extend,Discriminator discriminator,
    List resultMappings,Boolean autoMapping)

//通过建造者模式实例化resultMap,并注册到configuration.resultMaps中

ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
    .discriminator(discriminator)
    .build();

configuration.addResultMap(resultMap);

return resultMap;

总后通过ResultMap将mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素。

重点3: 解析select|insert|update|delete节点

//解析select、insert、update、delete节点
private void buildStatementFromContext(List list) {
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  buildStatementFromContext(list, null);
}

//处理所有的sql语句节点并注册至configuration对象
private void buildStatementFromContext(List 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语句节点,对应XML配置如下:

<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
  select
  <include refid="Base_Column_List" />
  from t_user where id = #{id,jdbcType=INTEGER}
select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
  delete from t_user where id = #{id,jdbcType=INTEGER}
delete>
<insert id="insert" parameterType="com.chj.mybatis.entity.TUser" >
  insert into t_user (id, user_name, real_name, sex, mobile, email, note, position_id)
  values (#{id,jdbcType=INTEGER}, #{userName,jdbcType=VARCHAR}, #{realName,jdbcType=VARCHAR},
    #{sex,jdbcType=TINYINT}, #{mobile,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR},
    #{note,jdbcType=VARCHAR}, #{positionId,jdbcType=INTEGER})
insert>

解析sql语句节点,对应java代码如下:

public void parseStatementNode() {
   String id = context.getStringAttribute("id");//获取sql节点的id
   String databaseId = context.getStringAttribute("databaseId");
   if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
     return;
   }
   /*获取sql节点的各种属性*/
   Integer fetchSize = context.getIntAttribute("fetchSize");
   Integer timeout = context.getIntAttribute("timeout");
   String parameterMap = context.getStringAttribute("parameterMap");
   String parameterType = context.getStringAttribute("parameterType");
   Class parameterTypeClass = resolveClass(parameterType);
   String resultMap = context.getStringAttribute("resultMap");
   String resultType = context.getStringAttribute("resultType");
   String lang = context.getStringAttribute("lang");
   LanguageDriver langDriver = getLanguageDriver(lang);
   Class resultTypeClass = resolveClass(resultType);
   String resultSetType = context.getStringAttribute("resultSetType");
   StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
   ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
   //根据sql节点的名称获取SqlCommandType(INSERT, UPDATE, DELETE, SELECT)
   String nodeName = context.getNode().getNodeName();
   SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
   boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
   boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
   boolean useCache = context.getBooleanAttribute("useCache", isSelect);
   boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
   // Include Fragments before parsing
   //在解析sql语句之前先解析节点
   XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
   includeParser.applyIncludes(context.getNode());
   // Parse selectKey after includes and remove them.
   //在解析sql语句之前,处理子节点,并在xml节点中删除
   processSelectKeyNodes(id, parameterTypeClass, langDriver);
   // Parse the SQL (pre: and were parsed and removed)
   //解析sql语句是解析mapper.xml的核心,实例化sqlSource,使用sqlSource封装sql语句
   SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
   String resultSets = context.getStringAttribute("resultSets");//获取resultSets属性
   String keyProperty = context.getStringAttribute("keyProperty");//获取主键信息keyProperty
   String keyColumn = context.getStringAttribute("keyColumn");///获取主键信息keyColumn
   //根据获取对应的SelectKeyGenerator的id
   KeyGenerator keyGenerator;
   String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
   keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
   //获取keyGenerator对象,如果是insert类型的sql语句,会使用KeyGenerator接口获取数据库生产的id;
   if (configuration.hasKeyGenerator(keyStatementId)) {
     keyGenerator = configuration.getKeyGenerator(keyStatementId);
   } else {
     keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
         configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
         ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
   }
   //通过builderAssistant实例化MappedStatement,并注册至configuration对象
   builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
       fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
       resultSetTypeEnum, flushCache, useCache, resultOrdered,
       keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
 }

最后通过MappedStatement映射器存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性。

 

4、映射器的关键类

1)Configuration映射器是Mybatis启动初始化的核心

Mybatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象中, Configuration是单例的,生命周期是应用级的。

第三章 MyBatis源码分析笔记1_第5张图片

 

public class Configuration {
  protected Environment environment;
  protected boolean safeRowBoundsEnabled;/* 是否启用行内嵌套语句**/
  protected boolean safeResultHandlerEnabled = true;
  /* 是否启用数据组A_column自动映射到Java类中的驼峰命名的属性**/
  protected boolean mapUnderscoreToCamelCase;
  /*当对象使用延迟加载时 属性的加载取决于能被引用到的那些延迟属性,否则,按需加载(需要的是时候才去加载)**/
  protected boolean aggressiveLazyLoading;
  /*是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true **/
  protected boolean multipleResultSetsEnabled = true;
  /*-允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false**/
  protected boolean useGeneratedKeys;
  /* 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。**/
  protected boolean useColumnLabel = true;
  /*配置全局性的cache开关,默认为true**/
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls;
  protected boolean useActualParamName = true;
  protected boolean returnInstanceForEmptyRow;
  /* 日志打印所有的前缀 **/
  protected String logPrefix;
  /* 指定 MyBatis 所用日志的具体实现,未指定时将自动查找**/
  protected Class extends Log> logImpl;
  protected Class extends VFS> vfsImpl;
  /* 设置本地缓存范围,session:就会有数据的共享,statement:语句范围,这样不会有数据的共享**/
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  /* 设置但JDBC类型为空时,某些驱动程序 要指定值**/
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  /* 设置触发延迟加载的方法**/
  protected Set lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
  protected Integer defaultStatementTimeout; /* 设置驱动等待数据响应超时数**/
  protected Integer defaultFetchSize; /* 设置驱动返回结果数的大小**/
  /* 执行类型,有simple、resue及batch**/
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  /*指定 MyBatis 应如何自动映射列到字段或属性*/
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
  protected Properties variables = new Properties();
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  /*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 String databaseId;
  // Used to create Configuration for loading deserialized unread properties.
  protected Class configurationFactory;
  /*插件集合*/
  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 mappedStatements = new StrictMap<>("Mapped Statements collection");
  /*mapper文件中配置cache节点的 二级缓存*/
  protected final Map caches = new StrictMap<>("Caches collection");
  /*mapper文件中配置的所有resultMap对象  key为命名空间+ID*/
  protected final Map resultMaps = new StrictMap<>("Result Maps collection");
  protected final Map parameterMaps = new StrictMap<>("Parameter Maps collection");
  /*mapper文件中配置KeyGenerator的insert和update节点,key为命名空间+ID*/
  protected final Map keyGenerators = new StrictMap<>("Key Generators collection");
  /*加载到的所有*mapper.xml文件*/
  protected final Set loadedResources = new HashSet<>();
  /*mapper文件中配置的sql元素,key为命名空间+ID*/
  protected final Map sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
  protected final Collection incompleteStatements = new LinkedList<>();
  protected final Collection incompleteCacheRefs = new LinkedList<>();
  protected final Collection incompleteResultMaps = new LinkedList<>();
  protected final Collection incompleteMethods = new LinkedList<>();
  protected final Map cacheRefMap = new HashMap<>();
  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

// 初始化的默认配置参数
  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

2)MapperRegistrymapper接口动态代理工厂类的注册中心。

在MyBatis中通过mapperProxy实现InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象。

public class MapperRegistry {
  private final Configuration config;//config对象,mybatis全局唯一的
  //记录了mapper接口与对应MapperProxyFactory之间的关系
  private final Map, MapperProxyFactory> knownMappers = new HashMap<>();

3)ResultMap映射器分析

用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素。

第三章 MyBatis源码分析笔记1_第6张图片

 

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

4)MappedStatement映射器分析

用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性。

第三章 MyBatis源码分析笔记1_第7张图片

 

public final class MappedStatement {
  private String resource;//节点的完整的id属性,包括命名空间
  private Configuration configuration;
  private String id;//节点的id属性
  private Integer fetchSize;//节点的fetchSize属性,查询数据的条数
  private Integer timeout;//节点的timeout属性,超时时间
  private StatementType statementType;//节点的statementType属性,默认值:StatementType.PREPARED;疑问?
  private ResultSetType resultSetType;//节点的resultSetType属性,jdbc知识
  private SqlSource sqlSource;//节点中sql语句信息
  private Cache cache;//对应的二级缓存
  private ParameterMap parameterMap;//已废弃
  private List resultMaps;//节点的resultMaps属性
  private boolean flushCacheRequired;//节点的flushCache属性是否刷新缓存
  private boolean useCache;//节点的useCache属性是否使用二级缓存
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;//sql语句的类型,包括:INSERT, UPDATE, DELETE, SELECT
  private KeyGenerator keyGenerator;//节点keyGenerator属性
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;//是否有嵌套resultMap
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;//多结果集使用

5)SqlSource映射器分析

mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行。

public interface SqlSource {
  BoundSql getBoundSql(Object parameterObject);
}

public class BoundSql {
  private final String sql;
  private final List parameterMappings;
  private final Object parameterObject;
  private final Map additionalParameters;
  private final MetaObject metaParameters;
  public BoundSql(Configuration configuration, String sql, List parameterMappings,

  Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }

5、MyBatis初始化过程

第三章 MyBatis源码分析笔记1_第8张图片

 

第一步:初始化阶段:

读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作;

// org.apache.ibatis.session.Configuration

Configuration  <-- XMLConfigBuilder <-- mybatis-config.xml

// 读取mybatis配置文件创SqlSessionFactory

sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

 build(parser.parse());  

// XMLConfigBuilder.parse()

parseConfiguration(parser.evalNode("/configuration"));

// 配置文件中的各个配置属性对应里面的各个方法,也对应着具体每个解析类

//解析节点

propertiesElement(root.evalNode("properties"));

//解析节点

Properties settings = settingsAsProperties(root.evalNode("settings"));

loadCustomVfs(settings);

//解析节点

typeAliasesElement(root.evalNode("typeAliases"));

//解析节点

pluginElement(root.evalNode("plugins"));

//解析节点

objectFactoryElement(root.evalNode("objectFactory"));

//解析节点

objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

//解析节点

reflectorFactoryElement(root.evalNode("reflectorFactory"));

settingsElement(settings);//将settings填充到configuration

// read it after objectFactory and objectWrapperFactory issue #631

//解析节点

environmentsElement(root.evalNode("environments"));

//解析节点

databaseIdProviderElement(root.evalNode("databaseIdProvider"));

//解析节点

typeHandlerElement(root.evalNode("typeHandlers"));

// 解析 节点

mapperElement(root.evalNode("mappers"));

Mappers节点具体解析代码如下:

// 通过package方式解析

if ("package".equals(child.getName())) {

String mapperPackage = child.getStringAttribute("name");

configuration.addMappers(mapperPackage);

} else {

// xxxMapper.xml配置方式解析

String resource = child.getStringAttribute("resource");

// 通过url方式配置解析

String url = child.getStringAttribute("url");

// 通过class方式配置解析

String mapperClass = child.getStringAttribute("class");

// mapper配置文件的解析方法 XMLMapperBuilder

mapperParser.parse();

configurationElement(parser.evalNode("/mapper"));

//解析cache-ref节点

cacheRefElement(context.evalNode("cache-ref"));

// 重点1: 解析cache节点-------------------------------------

cacheElement(context.evalNode("cache"));

// 通过builderAssistant创建缓存对象,并添加至configuration

builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, 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.addCache(cache);

// 重点2: 解析resultMap节点

resultMapElements(context.evalNodes("/mapper/resultMap"));

resultMapElement(resultMapNode);

// 从xml文件中当前的resultMap中的所有字节点

List resultChildren = resultMapNode.getChildren();

// 创建resultMapping对象并加入resultMappings集合中

resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));

builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);

 // 实例化resultMap解析器

ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);

// 通过resultMap解析器实例化resultMap对象并注册到configuration集合中

return resultMapResolver.resolve();

assistant.addResultMap();

 

// 解析SQL节点

sqlElement(context.evalNodes("/mapper/sql"));

// 重点3: 解析select|insert|update|delete节点

buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

//MappedStatement

statementParser.parseStatementNode();

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout,

parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache,

resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource,

sqlCommandType)

MappedStatement statement = statementBuilder.build();

configuration.addMappedStatement(statement);

你可能感兴趣的:(MyBatis学习笔记)