MyBatis源码分析篇----初始化之XMLConfigBuilder#parse

源起

​ 在构建会话工厂类的时候,会解析全局配置文件,然后将相关信息存储值Configuration中;解析配置文件入口:org.apache.ibatis.builder.xml.XMLConfigBuilder#parse,所以我们这一篇博文呢,就以这个方法为切入口,分析一下MyBatis初始化的相关操作源码。

MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第1张图片

源码分析
1. XMLConfigBuilder#parseConfiguration
  • 首先parse()方法会调用parseConfiguration(XNode root),这里的XNode是指根节点configuration下所有的节点内容(parser.evalNode("/configuration")),这里解析XML采用的是XPath方法。

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第2张图片

  • 下面的代码是我们项目中经常用到的mybatis-conf.xml全局配置文件相关代码,接下来我们分析各节点的内容解析:

  • <configuration>
        
        <properties resource="db.properties"/>
        
        
        <settings>
            
            <setting name="cacheEnabled" value="true"/>
            
            <setting name="lazyLoadingEnabled" value="true"/>
            
            <setting name="multipleResultSetsEnabled" value="true"/>
            
            <setting name="logImpl" value="STDOUT_LOGGING"/>
            
        settings>
    
        
        <typeAliases>
            
            
            <package name="org.mybatis.example.pojo"/>
        typeAliases>
        
        
        <plugins>
            <plugin interceptor="org.mybatis.example.ExamplePlugin">
                <property name="someProperty" value="100"/>
            plugin>
        plugins>
     
    	
        <environments default="development">
            <environment id="development">
                
                <transactionManager type="JDBC"/>
                
                <dataSource type="POOLED">
                    
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                dataSource>
            environment>
        environments>
      
    	
        <mappers>
            
            
            <package name="org.mybatis.example.dao"/>
        mappers>
    configuration>
    
2. XMLConfigBuilder#propertiesElement
  • org.apache.ibatis.builder.xml.XMLConfigBuilder#propertiesElement,该方法是解析properties节点内容;

  • <properties resource="org/mybatis/example/db.properties">
          <property name="username" value="dev_user"/>
          <property name="password" value="F2Fa3!33TYyg"/>
    properties>
    
    
    <properties resource="db.properties"/>
    
3. XMLConfigBuilder#settingsAsProperties
  • org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsAsProperties,该方法主要用来解析节点的内容;

  • 这是MyBatis中极为重要的调整设置,它们会改变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="autoMappingBehavior" value="PARTIAL"/>
        <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <setting name="defaultStatementTimeout" value="25"/>
        <setting name="defaultFetchSize" value="100"/>
        <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>
    
  • 可查看官网介绍说明:settings

  • 结合org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement方法

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第3张图片

4. XMLConfigBuilder#typeAliasesElement
  • org.apache.ibatis.builder.xml.XMLConfigBuilder#typeAliasesElement类型别名,节点解析;

  • 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写;

  • <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>
    
    <typeAliases>
        <package name="org.mybatis.example.pojo"/>
    typeAliases>
    
  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第4张图片

5. XMLConfigBuilder#pluginElement
  • MyBatis允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis允许使用插件来拦截的方法调用包括:

    • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    • ParameterHandler (getParameterObject, setParameters)
    • ResultSetHandler (handleResultSets, handleOutputParameters)
    • StatementHandler (prepare, parameterize, batch, update, query)
  • 这也就是我们可以进行插件扩展的地方,比如大名鼎鼎的分页插件:PageHelper就是利用插件原理实现分页的。

  • 配置设置:

  • <plugins>
        <plugin interceptor="org.mybatis.example.ExamplePlugin">
            <property name="someProperty" value="100"/>
        plugin>
    plugins>
    
  • @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
    public class ExamplePlugin implements Interceptor {
        private Properties properties = new Properties();
        public Object intercept(Invocation invocation) throws Throwable {
            // implement pre processing if need
            Object returnObject = invocation.proceed();
            // implement post processing if need
            return returnObject;
        }
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    }
    
  • 上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor是负责执行底层映射语句的内部对象。

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第5张图片

6. XMLConfigBuilder#objectFactoryElement
  • org.apache.ibatis.builder.xml.XMLConfigBuilder#objectFactoryElement
  • 每次MyBatis创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作
  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第6张图片
7. XMLConfigBuilder#environmentsElement
  • org.apache.ibatis.builder.xml.XMLConfigBuilder#environmentsElement,环境配置,解析节点

  • <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>
    
  • 注意一些关键点:

    • 默认使用的环境 ID(比如:default=“development”)。
    • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
    • 事务管理器的配置(比如:type="JDBC")。
    • 数据源的配置(比如:type=“POOLED”)。
  • 默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。

7.1. XMLConfigBuilder#transactionManagerElement
  • 解析节点,设置事务管理器信息;

  • MyBatis中有两种类型的事务管理器(也就是type="[JDBC|MANAGED]"):

    • JDBC– 这个配置直接使用了JDBC的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

    • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将closeConnection属性设置为 false 来阻止默认的关闭行为。

    • <transactionManager type="MANAGED">
          <property name="closeConnection" value="false"/>
      transactionManager>
      
  • 如果使用Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置

7.2. XMLConfigBuilder#dataSourceElement
  • 数据源解析

  • dataSource元素使用标准的JDBC 数据源接口来配置JDBC连接对象的资源。

  • 大多数MyBatis应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
  • 有三种内建的数据源类型(也就是type="[UNPOOLED|POOLED|JNDI]");
    • UNPOOLED: 这个数据源的实现会每次请求时打开和关闭连接;
    • POOLED:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间;
    • JNDI: 这个数据源实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的数据源引用。
8. XMLConfigBuilder#typeHandlerElement
  • 类型处理器

  • MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成Java类型

  • <typeHandlers>
        <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
    typeHandlers>
    
    <typeHandlers>
        <package name="org.mybatis.example"/>
    typeHandlers>
    
9. XMLConfigBuilder#mapperElement
  • 至此,MyBatis的行为已经由上述元素配置完成,现在我们就要定义SQL映射语句了。 所以,首先我们需要告诉MyBatis到哪里去找到这些语句。

  • 
    <mappers>
        <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
        <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
        <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    mappers>
    
    
    <mappers>
        <mapper url="file:///var/mappers/AuthorMapper.xml"/>
        <mapper url="file:///var/mappers/BlogMapper.xml"/>
        <mapper url="file:///var/mappers/PostMapper.xml"/>
    mappers>
    
    
    <mappers>
        <mapper class="org.mybatis.builder.AuthorMapper"/>
        <mapper class="org.mybatis.builder.BlogMapper"/>
        <mapper class="org.mybatis.builder.PostMapper"/>
    mappers>
    
    
    <mappers>
        <package name="org.mybatis.builder"/>
    mappers>
    
  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第7张图片

  • 下面我们来分析一下源码。

9.1. Configuration#addMappers
  • 
    <mappers>
        <package name="org.mybatis.builder"/>
    mappers>
    
    
    <mappers>
        <mapper class="org.mybatis.builder.AuthorMapper"/>
        <mapper class="org.mybatis.builder.BlogMapper"/>
        <mapper class="org.mybatis.builder.PostMapper"/>
    mappers>
    
  • 下面就这两种方式的加载,分析一下源码

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第8张图片

  • 上面两个方法最终都会间接或直接的调用到org.apache.ibatis.binding.MapperRegistry#addMapper

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第9张图片

  • Mapper接口存储在名为knownMappersMap中,key值为接口类型,value是其接口类型的映射器代理工厂类;该代理类会在调用mapper接口中方法时,获取接口的代理类MapperProxy

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第10张图片

  • 下来会调用org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#loadXmlResource方法

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第11张图片

  • 上图代码中**String xmlResource = type.getName().replace('.', '/') + ".xml";**,是将包名中的“.”替换为“/”,既:在同包路径下找xml文件;如果运行报诸如:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found).....的错误:需要注意查看同包下是否有对应xml,如果有,则需要看编译时是否将xml编辑进去了,如果target中没有编译后的xml,则需要在pom文件(Maven管理的项目)中配置:

  • <build>
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
                
                
            resource>
        resources>
    build>
    
  • 获取到XML文件流之后,会调用org.apache.ibatis.builder.xml.XMLMapperBuilder#parse进行解析。

  • 针对上面的四种配置方式,其实最终都会走到下面这个方法

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第12张图片

9.2. XMLMapperBuilder#configurationElement
  • org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement方法执行xml配置文件的解析,构建SQL语句;

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第13张图片

  • MyBatis源码分析篇----初始化之XMLConfigBuilder#parse_第14张图片

  • 最终代码会走向org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode,进行构建MappedStatement,并将其添加到Configuration#mappedStatements中去。

结语

​ 至此,我们构建好了:执行sqlExecutor、保存了SQL信息的MappedStatement等等信息,然后当我们调用mapper.queryById(String)的时候,首先会在Configuration中的mappedStatements中获取一个对应类型的MappedStatement,然后会使用executor.query去执行查询操作。

公众号推荐

微信公众号:从Demo到折腾源码
微信号:albert_ztym
从demo到手撕源码

你可能感兴趣的:(MyBatis,mybatis,java,mysql)