mybatis源码分析(一) 配置解析过程

mybatis源码分析(一) 配置解析过程

一丶从加载mybatis-config.xml开始

  使用SqlSessionFactoryBuilder解析mybatis-config.xml, 构造SqlSession

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        this.sqlSession=sqlSessionFactory.openSession();
        this.userMapper=sqlSession.getMapper(UserMapper.class);

  mybatis-config.xml:

<?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>
    
    <!-- 配置外部属性 -->
    <properties resource="datasource.properties" />
    
    <!-- 添加日志实现 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        
        <!-- 是否开启下划线和驼峰式的自动转换, http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Auto-mapping -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
        
    
    <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="com/ttx/example/mapper/UserMapper.xml"/>
        <mapper resource="com/ttx/example/mapper/UserResultMapper.xml"/>
        <mapper resource="com/ttx/example/mapper/DynamicSqlUserMapper.xml"/>
        <mapper resource="com/ttx/example/mapper/UserSqlProviderMapper.xml"/>
        <mapper resource="com/ttx/example/mapper/UserCacheMapper.xml"/>
    </mappers>
    
</configuration>

二丶SqlSessionFactoryBuilder#build() 解析配置构建SqlSessionFactory

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
// 委托给XMLConfigBuilder解析mybatis-config.xml XMLConfigBuilder parser
= new XMLConfigBuilder(inputStream, environment, properties);
    // 调用parser.parse()解析Configuration
return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }

三丶XMLConfigBuilder#parse() 解析xml配置

  // 解析方法, 解析配置
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));  //根节点为configuration
    return configuration;
  }
 private void parseConfiguration(XNode root) {

    // 解析配置过程, 代码写的有点像spring中的某个方法,
    // 分出多个配置元素的解析方法
  // 这里的方法对应mybatis-config.xml的各个子元素
  // 将mybatis-config.xml中的各个元素解析后放进Configuration中

try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); // settings Properties settings = settingsAsProperties(root.evalNode("settings")); // 加载虚拟文件系统 loadCustomVfs(settings); // 加载日志实现 loadCustomLogImpl(settings); // 类型别名 typeAliasesElement(root.evalNode("typeAliases")); // 插件 pluginElement(root.evalNode("plugins")); // 对象工厂 objectFactoryElement(root.evalNode("objectFactory")); // 对象包装工厂 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 反射器工厂 reflectorFactoryElement(root.evalNode("reflectorFactory")); // ------------------------ // 设置Configuration中的属性, // settings, typeAliases, plugins, objectFactory, objectWrapperFactory, reflectorFactory 对应Configuration中的属性 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 环境配置 environmentsElement(root.evalNode("environments")); // 数据库id databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 类型处理器 typeHandlerElement(root.evalNode("typeHandlers")); // 重点 // mappers配置路径 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }

四丶重点分析XMLConfigBuilder#mapperElement()解析mapper.xml, 包装成mappedStatement

  // 加载mapper
  // =============================================
  // mybatis加载Mapper的总入口
  // 1. 通过配置包查找对应的Mapper接口添加, mapper.xml配置文件应在同一个包下
  // 2. 通过配置resource= "mapper.xml" 路径来加载 
  // =============================================
  private void mapperElement(XNode parent) throws Exception {  // mappers
    if (parent != null) {
      for (XNode child : parent.getChildren()) {

        if ("package".equals(child.getName())) { // 1. 可以指定 "包" 级别
          String mapperPackage = child.getStringAttribute("name");
          // 会加载包名下的所有类, 并使用注解构建器解析, 先解析mapper.xml配置文件, 后再解析mapper.class// 只能通过指定xml配置文件来查找, 如果指定类, 则对应的配置文件需要放在和类同一个包下, 或者classpath路径下
          configuration.addMappers(mapperPackage);

        } else {
            // 单个指定
            // resource, url 指定对应的xml文件, class指定对应的类
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

五丶XMLMapperBuilder#parse()

  /**
   * 主要方法, parse
   */
  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      // 加载解析mapper.xml中各个元素, 包括解析statement, resultMap
      // warning 在MapperProxy中使用MapperMethod封装调用, 会将调用参数转成合适的类型,绑定对应数据
      configurationElement(parser.evalNode("/mapper"));
      // 设置已加载解析的资源
      configuration.addLoadedResource(resource);
      // 加载解析对应的mapper接口, 将接口放到mapper注册表中  
      bindMapperForNamespace();
    }

    // 加载解析尚未加载完的数据
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }
  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);  // 在configuration添加已加载资源
          configuration.addMapper(boundType);  // 在configuration添加mapper接口, 然后在mapperRegistry中解析添加
        }
      }
    }
  }

六丶将mapper添加进configuration中的mapperRegistry中, 期间由mapperRegistry解析

  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 {
        // 添加进knownMappers, 值为对应的代理工厂(MapperProxyFactory)的类
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.

        // 使用注解构建器构建, 会先解析mapper.xml配置文件, 然后再解析mapper.class文件中的注解
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
人生没有彩排,每一天都是现场直播
原文地址:https://www.cnblogs.com/timfruit/p/11462624.html