mybatis源码(一)

173 阅读5分钟

1、启动mybtais

项目结构:

image.png

1.1 maven依赖

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.0</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

1.2 配置文件

mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>


    <settings>
        <setting name="logImpl" value="LOG4J"/>

    </settings>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="dyzwj"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.dyzwj.mybatis.mapper.UserMapper" >

    <select id="selectAll" resultType="java.util.Map" parameterType="java.util.Map">
        select * from user where username = #{username} and age = #{age}
    </select>

<!--    <cache eviction="" flushInterval=""></cache>-->

    <select id="select" resultType="java.util.Map" parameterType="java.util.Map">
        select * from user order by #{username}
    </select>

</mapper>

1.3 实体类和mapper接口

实体类

public class User {
    
    private String username;
    private int age;
    
    //省略getter setter方法
}

mapper接口

public interface UserMapper {

    List<Map<String,Object>> selectAll(Map<String,Object> param);

//    List<Map<String,Object>> selectAll(String username,String age);

    List<Map<String,Object>> select(Map<String,Object> param);
}

1.4 启动类

public class Test1 {

    public static void main(String[] args) throws Exception {
        String resource = "mybatis.xml";
        
        //加载mybatis配置文件
        InputStream resourceAsStream = Test1.class.getResourceAsStream("/" + resource);


        InputStream resourceAsStream1 = Test1.class.getClassLoader().getResourceAsStream(resource);
        System.out.println(resourceAsStream);
        System.out.println(resourceAsStream.hashCode());
        System.out.println(resourceAsStream1);

//        加载配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        Configuration configuration = sqlSession.getConfiguration();
        /**
         * 通过动态代理  去帮我们执行sql
         */
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        Map<String,Object> param = new HashMap<>();
        param.put("username","zwj");
        param.put("age",22);

        mapper.select(param);

        List<Map<String, Object>> maps = mapper.selectAll(param);
        //因为一级缓存 这里不会调用两次
//        List<Map<String, Object>> maps1 = mapper.selectAll(param);

        //因为二级缓存 这里不会调用两次
//        List<Map<String, Object>> maps2 = mapper1.selectAll(param);
//        List<Map<String, Object>> maps = mapper.selectAll("zwj","1");
        maps.forEach( x -> System.out.println(x));

    }
}

2 mybatis启动流程

2.1 加载mybatis配置文件

SqlSessionFactoryBuilder#build()

public class SqlSessionFactoryBuilder {
   
    public SqlSessionFactory build(InputStream inputStream) {
      //继续
      return build(inputStream, null, null);
    }
    

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      try {
        /**
         * 解析config.xml(mybatis解析xml使用的java dom)
         */
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        /**
         * parse:解析mybatis.xml里面的节点
         */
         //调用下面重载的方法
        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.
        }
      }
    }
    
    
    //mybatis配置文件解析完成后 会将所有信息封装的全局唯一的Configuraion对象中
    public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
    }


}

2.2 解析mybatis配置文件

XMLConfigBuilder#parse:解析mybatis.xml里面的节点

public class XMLConfigBuilder extends BaseBuilder {
  
    /**
     * 解析配置
     * @return
     */
    public Configuration parse() {
      if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
      }
      /**
       * 保证xml只解析一次
       */
      parsed = true;

      /**
       * 根节点是<configuration>
       */
      parseConfiguration(parser.evalNode("/configuration"));
      return configuration;
    }
    
    
    //  <?xml version="1.0" encoding="UTF-8" ?>
    //  <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    //  "http://mybatis.org/dtd/mybatis-3-config.dtd">
    //  <configuration>
    //  <environments default="development">
    //  <environment id="development">
    //  <transactionManager type="JDBC"/>
    //  <dataSource type="POOLED">
    //  <property name="driver" value="${driver}"/>
    //  <property name="url" value="${url}"/>
    //  <property name="username" value="${username}"/>
    //  <property name="password" value="${password}"/>
    //  </dataSource>
    //  </environment>
    //  </environments>
    //  <mappers>
    //  <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    //  </mappers>
    //  </configuration>
      private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          /**
           * 2.3 解析properties标签
           */
          propertiesElement(root.evalNode("properties"));
          /**
           *  2.4 解析setting标签 读取settings属性到Properties
           */
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          
          //2.5 加载日志实现
          loadCustomLogImpl(settings);
          /**
           * 2.6 解析typeAlias标签 别名  
           */
          typeAliasesElement(root.evalNode("typeAliases"));
          /**
           *  2.7 插件解析
           */
          pluginElement(root.evalNode("plugins"));
          /**
           *  2.8、解析对象工厂
           */
          objectFactoryElement(root.evalNode("objectFactory"));


          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          //MateObject
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          /**
           * 2.9 给Configuration设置属性
           */
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          /**
           * 2.10 解析environments标签
           */
          environmentsElement(root.evalNode("environments"));

          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          /**
           * 2.11 解析类型处理器
           */
          typeHandlerElement(root.evalNode("typeHandlers"));
          /**
           * 2.12 解析mapper文件  关注
           */
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }
}

2.3 解析properties标签

/**
 * //1.properties
 *   //<properties resource="org/mybatis/example/config.properties">
 *   //    <property name="username" value="dev_user"/>
 *   //    <property name="password" value="F2Fa3!33TYyg"/>
 *   //</properties>
 */

/**
 * //1.在 properties 元素体内指定的属性首先被读取。
 * //2.从类路径下资源或 properties 元素的 url 属性中加载的属性第二被读取,它会覆盖已经存在的完全一样的属性。
 * //3.作为方法参数传递的属性最后被读取, 它也会覆盖任一已经存在的完全一样的属性,这些属性可能是从 properties
 *       元素体内和资源/url 属性中加载的。
 */
private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    /**
     * 1、得到所有的<property></>标签的属性
     */
    Properties defaults = context.getChildrenAsProperties();
    String resource = context.getStringAttribute("resource");
    String url = context.getStringAttribute("url");
    if (resource != null && url != null) {
      throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
    }
    /**
     * 2、然后查找resource或者url,加入前面的Properties
     */
    if (resource != null) {
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    /**
     * 3.Variables也全部加入Properties
     */
    Properties vars = configuration.getVariables();
    if (vars != null) {
      defaults.putAll(vars);
    }
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}

2.4 解析setting标签

/**
 *
 *   //这些是极其重要的调整, 它们会修改 MyBatis 在运行时的行为方式
 * //<settings>
 * //  <setting name="cacheEnabled" value="true"/>
 * //  <setting name="lazyLoadingEnabled" value="true"/>
 * //  <setting name="multipleResultSetsEnabled" value="true"/>
 * //  <setting name="useColumnLabel" value="true"/>
 * //  <setting name="useGeneratedKeys" value="false"/>
 * //  <setting name="enhancementEnabled" value="false"/>
 * //  <setting name="defaultExecutorType" value="SIMPLE"/>
 * //  <setting name="defaultStatementTimeout" value="25000"/>
 * //  <setting name="safeRowBoundsEnabled" value="false"/>
 * //  <setting name="mapUnderscoreToCamelCase" value="false"/>
 * //  <setting name="localCacheScope" value="SESSION"/>
 * //  <setting name="jdbcTypeForNull" value="OTHER"/>
 * //  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
 * //</settings>
 */
private Properties settingsAsProperties(XNode context) {
  if (context == null) {
    return new Properties();
  }
  Properties props = context.getChildrenAsProperties();
  // Check that all settings are known to the configuration class
  MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  for (Object key : props.keySet()) {
    /**
     * 检查下是否在Configuration类里都有相应的setter方法(没有拼写错误)
     */
    if (!metaConfig.hasSetter(String.valueOf(key))) {
      throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
    }
  }
  return props;
}

2.5 日式实现

private void loadCustomLogImpl(Properties props) {
  Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
  configuration.setLogImpl(logImpl);
}

2.6 解析类型别名

/**
 *   //2.类型别名
 * //<typeAliases>
 * //  <typeAlias alias="Author" type="domain.blog.Author"/>
 * //  <typeAlias alias="Blog" type="domain.blog.Blog"/>
 * //  <typeAlias alias="Comment" type="domain.blog.Comment"/>
 * //  <typeAlias alias="Post" type="domain.blog.Post"/>
 * //  <typeAlias alias="Section" type="domain.blog.Section"/>
 * //  <typeAlias alias="Tag" type="domain.blog.Tag"/>
 * //</typeAliases>
 * //or
 * //<typeAliases>
 * //  <package name="domain.blog"/>
 * //</typeAliases>
 */
private void typeAliasesElement(XNode parent) {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String typeAliasPackage = child.getStringAttribute("name");
        /**
         * 1、如果是package 调用TypeAliasRegistry.registerAliases,去包下找所有类,
         * 然后注册别名(有@Alias注解则用,没有则取类的simpleName)
         */
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } else {
        String alias = child.getStringAttribute("alias");
        String type = child.getStringAttribute("type");
        try {
          /**
           *  2、根据Class名字来注册类型别名
           */
          Class<?> clazz = Resources.classForName(type);
          if (alias == null) {
            typeAliasRegistry.registerAlias(clazz);
          } else {
            typeAliasRegistry.registerAlias(alias, clazz);
          }
        } catch (ClassNotFoundException e) {
          throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
        }
      }
    }
  }
}

2.7 解析插件

/**
 * 插件解析
 *  //MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis 允许使用插件来拦截方法调用
 * //<plugins>
 * //  <plugin interceptor="org.mybatis.example.ExamplePlugin">
 * //    <property name="someProperty" value="100"/>
 * //  </plugin>
 * //</plugins>
 *
 */
private void pluginElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      String interceptor = child.getStringAttribute("interceptor");
      Properties properties = child.getChildrenAsProperties();
      /**
       * 创建拦截器对象
       *  设置属性
       */
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
      interceptorInstance.setProperties(properties);
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

2.8 解析对象工厂

/**
 *   //4.对象工厂,可以自定义对象创建的方式
 * //<objectFactory type="org.mybatis.example.ExampleObjectFactory">
 * //  <property name="someProperty" value="100"/>
 * //</objectFactory>
 * @param context
 * @throws Exception
 */
private void objectFactoryElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type");
    Properties properties = context.getChildrenAsProperties();
    /**
     * 创建对象工厂
     */
    ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
    /**
     * 创建对象
     */
    factory.setProperties(properties);
    configuration.setObjectFactory(factory);
  }
}

2.9 给Configuration设置属性

private void settingsElement(Properties props) {
  /**
   *自动映射
   */
  configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
  /**
   *
   */
  configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
  /**
   * 缓存 二级缓存默认开启 但还需要每个mapper手动开启二级缓存
   */
  configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
  /**
   * 代理工厂
   */
  configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
  /**
   *  懒加载
   */
  configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
  /**
   * 延迟加载时,每种属性是否还要按需加载
   */
  configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
  /**
   * 允不允许多种结果集从一个单独 的语句中返回
   */
  configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
  /**
   * 使用列标签代替列名
   */
  configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
  /**
   * 允许 JDBC 支持生成的键
   */
  configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
  /**
   *  配置默认的执行器
   */
  configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  /**
   * 超时时间
   */
  configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));

  configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
  /**
   * 是否将DB字段自动映射到驼峰式Java属性(A_COLUMN-->aColumn)
   */
  configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
  /**
   * 嵌套语句上使用RowBounds
   */
  configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  /**
   * 默认用sqlsession级别的缓存(一级缓存)
   */
  configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
  /**
   * 为null值设置jdbctype
   */
  configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
  /**
   * Object的哪些方法将触发延迟加载
    */
  configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  /**
   * 使用安全的ResultHandler
   */
  configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
  /**
   * 动态SQL生成语言所使用的脚本语言
   */
  configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
  /**
   *
   */
  configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
  /**
   * 当结果集中含有Null值时是否执行映射对象的setter或者Map对象的put方法。此设置对于原始类型如int,boolean等无效。
   */
  configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  /**
   *
   */
  configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
  configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
  /**
   * logger名字的前缀
   */
  configuration.setLogPrefix(props.getProperty("logPrefix"));
  /**
   * 配置工厂
   */
  configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}

2.10 解析environments标签

/**
 * //  <environments default="development">
 * //    <environment id="development">
 * //      <transactionManager type="JDBC">
 * //        <property name="..." value="..."/>
 * //      </transactionManager>
 * //      <dataSource type="POOLED">
 * //        <property name="driver" value="${driver}"/>
 * //        <property name="url" value="${url}"/>
 * //        <property name="username" value="${username}"/>
 * //        <property name="password" value="${password}"/>
 * //      </dataSource>
 * //    </environment>
 * //  </environments>
 */
private void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    if (environment == null) {
      environment = context.getStringAttribute("default");
    }
    for (XNode child : context.getChildren()) {
      String id = child.getStringAttribute("id");
      /**
       * 循环比较id是否就是指定的environment
       */
      if (isSpecifiedEnvironment(id)) {
        /**
         * 事务管理器
         */
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        /**
         * 数据源
         */
        DataSource dataSource = dsFactory.getDataSource();
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        configuration.setEnvironment(environmentBuilder.build());
      }
    }
  }
}

2.11 解析类型处理器

/** 类型处理器
 * //  <typeHandlers>
 * //    <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
 * //  </typeHandlers>
 * //or
 * //  <typeHandlers>
 * //    <package name="org.mybatis.example"/>
 * //  </typeHandlers>
 *   private void typeHand
 * @param parent
 */
private void typeHandlerElement(XNode parent) {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String typeHandlerPackage = child.getStringAttribute("name");
        /**
         * 如果是package 调用TypeHandlerRegistry.register,去包下找所有类
         */
        typeHandlerRegistry.register(typeHandlerPackage);
      } else {
        String javaTypeName = child.getStringAttribute("javaType");
        String jdbcTypeName = child.getStringAttribute("jdbcType");
        String handlerTypeName = child.getStringAttribute("handler");
        Class<?> javaTypeClass = resolveClass(javaTypeName);
        JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
        Class<?> typeHandlerClass = resolveClass(handlerTypeName);
        /**
         * 手动注册
         */
        if (javaTypeClass != null) {
          if (jdbcType == null) {
            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
          } else {
            typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
          }
        } else {
          typeHandlerRegistry.register(typeHandlerClass);
        }
      }
    }
  }
}

2.12 扫描并解析mapper文件

扫描并解析mapper文件比较重要,下文单独进行说明