zoukankan      html  css  js  c++  java
  • 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);
            }
          }
        }
      }
    人生没有彩排,每一天都是现场直播
  • 相关阅读:
    一个有关Golang Deferred Function 执行顺序的问题
    Golang闭包的坑
    Golang new() vs make()
    修改linux文件的mtime
    Golang通过反射获取结构体的标签
    rpm --qf 命令
    elasticsearch 5.1 认证过期 (your license has expired)
    Heka GeoIpDecoder 配置
    Heka 编译安装后 运行报错 panic: runtime error: cgo argument has Go pointer to Go pointer
    github.com/oschwald/maxminddb-golang 安装报错
  • 原文地址:https://www.cnblogs.com/timfruit/p/11462624.html
Copyright © 2011-2022 走看看