• Mybatis源码解析(二):全局配置文件的解析


    Mybatis源码系列文章

    手写源码(了解源码整体流程及重要组件)

    Mybatis源码解析(一):环境搭建

    Mybatis源码解析(二):全局配置文件的解析

    Mybatis源码解析(三):映射配置文件的解析

    Mybatis源码解析(四):sql语句及#{}、${}的解析

    Mybatis源码解析(五):SqlSession会话的创建

    Mybatis源码解析(六):缓存执行器操作流程

    Mybatis源码解析(七):查询数据库主流程

    Mybatis源码解析(八):Mapper代理原理

    Mybatis源码解析(九):插件机制

    Mybatis源码解析(十):一级缓存和二级缓存



    一、加载配置文件

    环境搭建章节,通过传统方式测试如下:

    @Test
    public void test1() throws IOException {
    
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    
      SqlSession sqlSession = sqlSessionFactory.openSession();
    
      User user = sqlSession.selectOne("user.findUserById", 100);
    
      System.out.println(user);
    
      sqlSession.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第一步操作:Resources.getResourceAsStream(“sqlMapConfig.xml”)

    • 通过类加载器对配置文件进行加载,加载成了字节输入流,存到内存中
    • 注意:配置文件并没有被解析,只是转换成输入流对象

    二、会话工厂Build类的构建

    1、构建者设计模式

    • 创建对象方式:Dept tom = Dept.newBuilder().deptno(1).dname(“tom”).build();
    • 优点:将一个复杂对象每个部分的创建分离开,链式调用,思路清晰
    public class Dept {
        private Integer deptno;
        private String dname;
    
        //步骤5:实体类提供全参构造方法
        private Dept(Integer deptno, String dname) {
            this.deptno = deptno;
            this.dname = dname;
        }
    
        public static DeptBuilder newBuilder() { //步骤 2:提供返回Builder的静态方法
            return new DeptBuilder();
        }
    
        public static class DeptBuilder {  //步骤 1:定义Builder类
            //步骤3:提供和实体类一样的属性
            private Integer deptno;
            private String dname;
    
            //步骤4:提供设置属性的方法
            public DeptBuilder deptno(Integer deptno) {
                this.deptno = deptno;
                return this;
            }
    
            public DeptBuilder dname(String dname) {
                this.dname = dname;
                return this;
            }
    
            public Dept build(){ //步骤6:提供返回实体类的方法
                return new Dept(deptno,dname);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2、build构建重载方法

    第二步操作:new SqlSessionFactoryBuilder().build(resourceAsStream)

    • new SqlSessionFactoryBuilder():这里只是调用会话工厂构建类的无参方法,无其他动作
    • build方法涉及多个重载方法
      • inputStream:核心配置文件的字节输入流
      • environment:核心配置文件中标签的id值,build方法调用时候可以添加环境id,不传的话,后续会从标签的默认值中获取环境id值
      • properties:旧对象,已不再使用

    在这里插入图片描述
    在这里插入图片描述

    三、build构建方法:创建【核心配置文件解析类】

    build方法实现类

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
       try {
         // XMLConfigBuilder:用来解析XML配置文件
         // 使用构建者模式:好处:降低耦合、分离复杂对象的创建
         // 1.创建XPathParser解析器对象,根据inputStream解析成了document对象 2.创建全局配置对象Configuration对象
         XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    
         // parser.parse():使用XPATH解析XML配置文件,将配置文件封装到Configuration对象
         // 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)
         // parse():配置文件就解析完成了
         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.
         }
       }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1、BaseBuilder类图

    在这里插入图片描述

    • 这些子类基本上都是以Builder结尾,这里使用的是Builder建造者设计模式
    • 不同的Builder类对应不同组件的内容解析
    • XMLConfigBuilder:解析核心配置文件xml类;XMLMapperBuilder:解析实体映射配置文件xml类

    XMLConfigBuilder构造方法

    • XPathParser:将xml字节输入流转换为Document对象,存入XPathParser,而XPathParser对象引用放到XMLConfigBuilder对象parser属性中
    • 环境id也赋值到XMLConfigBuilder属性中
    • parsed属性:核心配置文件是否被解析,解析后会被赋值为true,表示只能解析一次
    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
      // XPathParser基于 Java XPath 解析器,用于解析 MyBatis中的配置文件
      this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }
    
    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
      //  创建Configuration对象,并通过TypeAliasRegistry注册一些Mybatis内部相关类的别名
      super(new Configuration());
      ErrorContext.instance().resource("SQL Mapper Configuration");
      this.configuration.setVariables(props);
      this.parsed = false;
      this.environment = environment;
      this.parser = parser;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、创建Configuration对象

    Configuration对象

    • 核心配置文件xml可以配置的标签如下

    • 核心配置文件将会被解析为Configuration配置类,而里面的标签则会被解析为对应的属性
      在这里插入图片描述

    • environment中包含statementId:“namespace.id”、事务工厂和数据源信息

    public class Configuration {
      protected Environment environment;
      // 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。默认为false
      protected boolean safeRowBoundsEnabled;
      // 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false
      protected boolean safeResultHandlerEnabled = true;
      // 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN
      // 到经典 Java 属性名 aColumn 的类似映射。默认false
      protected boolean mapUnderscoreToCamelCase;
      // 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认值false (true in ≤3.4.1)
      protected boolean aggressiveLazyLoading;
      // 是否允许单一语句返回多结果集(需要兼容驱动)。
      protected boolean multipleResultSetsEnabled = true;
      // 允许 JDBC 支持自动生成主键,需要驱动兼容。这就是insert时获取mysql自增主键/oracle sequence的开关。
      // 注:一般来说,这是希望的结果,应该默认值为true比较合适。
      protected boolean useGeneratedKeys;
      // 使用列标签代替列名,一般来说,这是希望的结果
      protected boolean useColumnLabel = true;
      // 是否启用缓存
      protected boolean cacheEnabled = true;
      // 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,
      // 这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
      protected boolean callSettersOnNulls;
      // 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,
      // 并且加上-parameters选项。(从3.4.1开始)
      protected boolean useActualParamName = true;
      //当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。
      // 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始)
      // 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。
      // 通常来说,我们会希望结果集不是null,单记录仍然是null
      protected boolean returnInstanceForEmptyRow;
    
      protected boolean shrinkWhitespacesInSql;
    
      // 指定 MyBatis 增加到日志名称的前缀。
      protected String logPrefix;
      // 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4j
      protected Class<? extends Log> logImpl;
      // 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口
      protected Class<? extends VFS> vfsImpl;
      protected Class<?> defaultSqlProviderType;
      // MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
      // 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
      // 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
      protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
      // 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,
      // 多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
      protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
      // 指定对象的哪个方法触发一次延迟加载。
      protected Set<String> lazyLoadTriggerMethods 
      							= new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
      // 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时
      protected Integer defaultStatementTimeout;
      // 为驱动的结果集设置默认获取数量。
      protected Integer defaultFetchSize;
      // SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);
      // BATCH 执行器将重用语句并执行批量更新。
      protected ResultSetType defaultResultSetType;
    
      // 默认执行器类型
      protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
      // 指定 MyBatis 应如何自动映射列到字段或属性。
      // NONE 表示取消自动映射;
      // PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。
      // FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
      protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
      // 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适
      protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior 
      													= AutoMappingUnknownColumnBehavior.NONE;
      // settings下的properties属性
      protected Properties variables = new Properties();
      // 默认的反射器工厂,用于操作属性、构造器方便
      protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
      // 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化
      protected ObjectFactory objectFactory = new DefaultObjectFactory();
      // 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类
      protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    
      // 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
      protected boolean lazyLoadingEnabled = false;
      // 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
      protected ProxyFactory proxyFactory = new JavassistProxyFactory(); 
    
      // MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
      protected String databaseId;
      /**
       * Configuration factory class.
       * Used to create Configuration for loading deserialized unread properties.
       *
       */
      protected Class<?> configurationFactory;
    
      protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    
      // mybatis插件列表
      protected final InterceptorChain interceptorChain = new InterceptorChain();
      protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
    
      // 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置
      // 比如时使用简写
      protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
      protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
      protected final Map<String, MappedStatement> mappedStatements = 
      new StrictMap<MappedStatement>("Mapped Statements collection")
          .conflictMessageProducer((savedValue, targetValue) ->
              ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
      protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
      protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
      protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
      protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
    
      protected final Set<String> loadedResources = new HashSet<>();
      protected final Map<String, XNode> sqlFragments 
      							= new StrictMap<>("XML fragments parsed from previous mappers");
    
      protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
      protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
      protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
      protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
    
      /*
       * A map holds cache-ref relationship. The key is the namespace that
       * references a cache bound to another namespace and the value is the
       * namespace which the actual cache is bound to.
       */
      protected final Map<String, String> cacheRefMap = new HashMap<>();
    
      public Configuration(Environment environment) {
        this();
        this.environment = environment;
      }
      
      ...
      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136

    Configuration构造函数

    • TypeAliasRegistry(类型别名注册器):注册以后就可以在xml中直接使用别名

    在这里插入图片描述

    public Configuration() {
      /**
       *  注册事务工厂的别名
       */
      // 利用java.sql.Connection对象完成对事务的提交
      typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
      // mybatis自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理
      typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    
      /**
       *  注册数据源的别名
       */
      // mybatis会从在应用服务器向配置好的JNDI数据源DataSource获取数据库连接。在生产环境中优先考虑这种方式
      typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
      // mybatis会创建一个数据库连接池,连接池的一个连接将会被用作数据库操作。一旦数据库操作完成,
      // mybatis会将此连接返回给连接池。在开发或测试环境中经常用到此方式
      typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
      // mybatis会为每一个数据库操作创建一个新的连接,并关闭它。该方式适用于只有小规模数量并发用户的简单应用程序上
      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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    JDBC事务工厂类

    public class JdbcTransactionFactory implements TransactionFactory {
    
      @Override
      public Transaction newTransaction(Connection conn) {
        return new JdbcTransaction(conn);
      }
    
      @Override
      public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
        return new JdbcTransaction(ds, level, autoCommit);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    构造函数:连接、隔离级别、是否自动提交

    public class JdbcTransaction implements Transaction {
    
      private static final Log log = LogFactory.getLog(JdbcTransaction.class);
    
      protected Connection connection;
      protected DataSource dataSource;
      protected TransactionIsolationLevel level;
      protected boolean autoCommit;
    
      public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
        dataSource = ds;
        level = desiredLevel;
        autoCommit = desiredAutoCommit;
      }
    
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 其他工厂类类似,xml中注册工厂类,使用时候通过工厂类的Class对象反射调用new方法创建需要的对象
    • super(new Configuration()); 最后创建完成的Configuration对象引用会放到抽象类BaseBuilder中
    • 再回头看BaseBuilder类图,也就是说configuration对象将要被解析到XMLConfigBuilder对象中

    四、build构建方法:调用【核心配置文件解析类】的解析方法

      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          // XMLConfigBuilder:用来解析XML配置文件
          // 使用构建者模式:好处:降低耦合、分离复杂对象的创建
          // 1.创建XPathParser解析器对象,根据inputStream解析成了document对象 2.创建全局配置对象Configuration对象
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    
          // parser.parse():使用XPATH解析XML配置文件,将配置文件封装到Configuration对象
          // 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)
          // parse():配置文件就解析完成了
          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.
          }
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    进入XMLConfigBuilder类的parse()解析方法中

    • parsed属性即XMLConfigBuilder构造函数中的值,默认false,代表只能解析一次,这里也应验了
    • “parser.evalNode(“/configuration”)”返回XNode对象,标签及其子标签都被封装到此对象中
    public Configuration parse() {
      if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
      }
      parsed = true;
      
      // parser.evalNode("/configuration"):通过XPATH解析器,解析configuration根节点
      // 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
      parseConfiguration(parser.evalNode("/configuration"));
    
      return configuration;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    进parseConfiguration方法解析XNode对象

    private void parseConfiguration(XNode root) {
      try {
        // 解析标签
        propertiesElement(root.evalNode("properties"));
        // 解析标签
        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"));
        settingsElement(settings);
        // 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);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    1、解析标签内容

    标签下的子标签很多,目前咱们配置文件只有标签和标签(映射配置文件-下一章单独来讲)

    进入environmentsElement(root.evalNode(“environments”));

    • root.evalNode(“environments”)返回XNode对象,标签及其子标签都被封装到context对象中
    • new SqlSessionFactoryBuilder().build(resourceAsStream)方法没传环境id,则会去获取默认环境id
    private void environmentsElement(XNode context) throws Exception {
      // 如果定义了environments
      if (context != null) {
        // 方法参数如何没有传递 environment,则解析sqlMapConfig.xml中的
        if (environment == null) {
          // 
          environment = context.getStringAttribute("default");
        }
    
        //遍历解析environment节点
        for (XNode child : context.getChildren()) {
          //获得id属性值
          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);
    
            // 将Environment存到configuraion中
            configuration.setEnvironment(environmentBuilder.build());
            break;
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    1)创建事务工厂对象

    在这里插入图片描述

    • child.evalNode(“transactionManager”):获取标签及其子标签内容,封装到XNode对象
    • 获取type属性值即JDBC,而JDBC别名对应JdbcTransactionFactory事务工厂类
    private TransactionFactory transactionManagerElement(XNode context) throws Exception {
      if (context != null) {
        String type = context.getStringAttribute("type");
        Properties props = context.getChildrenAsProperties();
        //通过Class对象构造函数创建对象
        TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
        factory.setProperties(props);
        return factory;
      }
      throw new BuilderException("Environment declaration requires a TransactionFactory.");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2)创建数据源工厂对象

    • 与创建事务工厂代码基本一样,获取PooledDataSourceFactory的Class对象,再反射获取工厂对象
    private DataSourceFactory dataSourceElement(XNode context) throws Exception {
      if (context != null) {
        String type = context.getStringAttribute("type");
        Properties props = context.getChildrenAsProperties();
        DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
        factory.setProperties(props);
        return factory;
      }
      throw new BuilderException("Environment declaration requires a DataSourceFactory.");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    构建Environment对象,赋值configuration

    • 通过构建者模式创建Environment对象
    • configuration.setEnvironment(environmentBuilder.build());将Environment存到configuraion中
    • 至此标签内容解析完毕
    public final class Environment {
      private final String id;
      private final TransactionFactory transactionFactory;
      private final DataSource dataSource;
      
      ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    五、默认会话工厂的构建

    • 核心配置文件解析完成返回Configuration对象,这里开始构建会话工厂对象
    public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
    }
    
    • 1
    • 2
    • 3
    • 默认会话工厂实现了SqlSessionFactory
    • SqlSessionFactory接口中有很多openSession重载方法,通过不同方式创建SqlSession
    • 具体实现,后面篇章再说
    • 回到第一章节,至此SqlSessionFactoryBuilder类build会话工厂任务完成
    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
      private final Configuration configuration;
    
      public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
      }
    
      @Override
      public SqlSession openSession() {
        // 调用openSessionFromDataSource 参数1:执行器类型  参数2:事务隔离级别  参数三:指定事务是否自动提交
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
      
      ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    总结

    • Mybatis入口从加载配置文件→输入流→Document对象→XNode对象
    • 从创建XMLConfigBuilder→Configuration→解析核心xml填充Configuration对象
  • 相关阅读:
    数据结构与算法基础(王卓)(5)
    很后悔,才发现这个API管理神器
    PixelSNAIL论文代码学习(3)——自注意力机制的实现
    如何在CSDN写笔记_写笔记前的插件安装
    rhcsa-文件内容显示
    基于飞机配电优化负荷管理系统研究(Matlab代码实现)
    UDP的MTU发现
    使用wireshark解析ipsec esp包
    单向的2.4G频段RF射频芯片-SI24R2E
    redis试题按知识点归类
  • 原文地址:https://blog.csdn.net/qq_35512802/article/details/127594570