Mybatis(四):源码分析-XML配置文件和构建SqlSessionFactory对象

Mybatis官方文档:https://mybatis.org/mybatis-3/zh/index.html

Mybatis源码(3.5.8-SNAPSHOT版本)下载:https://github.com/mybatis/mybatis-3

从官网Demo代码来入手:https://mybatis.org/mybatis-3/zh/getting-started.html

1、mybatis-config.xml配置文件解析 ,得到输入流;

底层使用java.io下的InputStream和Reader两个抽象类,通过Mybatis的Resources获取两种流的方法:

  • 字节输入流:Reader reader = Resources.getResourceAsReader(resource);
  • 字符流:InputStream inputStream = Resources.getResourceAsStream(resource);
// 官网Demo代码
// 配置文件路径
String resource = "org/mybatis/example/mybatis-config.xml";
// 读取mybatis-config.xml,通过Resources获取文件输入流
// 详情见Resources和ClassLoaderWrapper源码分析
InputStream inputStream = Resources.getResourceAsStream(resource);
// 解析xml配置文件,得到SqlSessionFactory,具体解析流程如下
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2、构建SqlSessionFactory对象

SqlSessionFactory为工厂类,单例模式,仅仅存储了Mybatis配置对象Configuration信息;就像名称一样,专门用来生产SqlSession对象,即会话;

SqlSessionFactoryBuilder:

  • SqlSessionFactoryBuilder类:SqlSessionFactoryBuilder没有构造方法,所有方法都是build()方法;
  • build方法都是返回SqlSessionFactory对象,所以可以知道构建SqlSessionFactory对象的方法为new SqlSessionFactoryBuilder().build();
  • build方法入参可知得到SqlSessionFactory对象只有三种方式InputStream(字节流)Reader(字符流)Configuration(配置类)
  • 无论InputStream还是Reader都是对mybatis-config.xml配置文件的解析,从最终生成SqlSessionFactory对象的build来看SqlSessionFactory必要信息是Configuration(配置类)!;所以InputStream和Reader入参的build中包含了XMLConfigBuilder对象来解析XML文件配置到Configuration配置类,后面介绍XMLConfigBuilder对象的具体源码。
  • 而最终生成的SqlSessionFactory对象是通过new DefaultSqlSessionFactory(config)

// SqlSessionFactoryBuilder源码
package org.apache.ibatis.session;


import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

public class SqlSessionFactoryBuilder {
    
    // SqlSessionFactoryBuilder来读取InputStream字节流的配置信息
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      try {
        // 创建XMLConfigBuilder对象
        // 对应下面源码分析3、SqlSessionFactoryBuilder().build()中构建XMLConfigBuilder对象
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // parser.parse()来生成Configuration配置对象,传给最终的build方法生成SqlSessionFactory对象
        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.
        }
      }
    }
    
    // SqlSessionFactoryBuilder来读取Reader字符流的配置信息
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
      try {
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        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.
        }
      }
    }
    
    // SqlSessionFactory对象生成的必要方法,必要参数为Configuration配置对象
    public SqlSessionFactory build(Configuration config) {
        // DefaultSqlSessionFactory为实现SqlSessionFactory接口的默认类
        return new DefaultSqlSessionFactory(config);
    }
      
}

DefaultSqlSessionFactory:

  • DefaultSqlSessionFactory类实现了SqlSessionFactory接口;
  • 唯一的成员变量configuration来管理Configuration配置对象;
  • 唯一的构造方法在对象实例化时来给configuration赋值;
  • 实现了SqlSessionFactory接口的各种openSession方法和getConfiguration方法;
  • openSession创建SqlSession对象的各种方法,在"源码分析构建SqlSeesion对象"中具体分析。
// DefaultSqlSessionFactory源码
package org.apache.ibatis.session.defaults;


// DefaultSqlSessionFactory类实现了SqlSessionFactory接口
public class DefaultSqlSessionFactory implements SqlSessionFactory {
    // 唯一的成员变量configuration来管理Configuration配置对象
    private final Configuration configuration;
    
    // 唯一的构造方法在对象实例化时来给configuration赋值
    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }
    
    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    } 
    
    @Override
    public Configuration getConfiguration() {
        return configuration;
    }

    ....
}

SqlSessionFactory接口:

  • 接口定义类,只定义了openSession()和getConfiguration()
  • openSession可以用来获取数据库SqlSession会话对象;
  • getConfiguration可以用来获取Mybatis整个配置信息对象Configuration。
// SqlSessionFactory 接口
package org.apache.ibatis.session;

import java.sql.Connection;

public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);

  SqlSession openSession(Connection connection);

  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();
}

3、SqlSessionFactoryBuilder().build()中构建XMLConfigBuilder对象

XMLConfigBuilder是用来解析XML配置文件,属于BaseBuilder子类,使用的是建造者设计模式,通过XMLConfigBuilder可以生成configuration对象,因为SqlSeesionFactory内唯一的成员变量configuration来管理Configuration配置对象。

继承BaseBuilder的类有:

  • MapperBuilderAssistant
  • SqlSourceBuilder
  • XMLConfigBuilder
  • XMLMapperBuilder
  • XMLStatementBuilder
  • XMLScriptBuilder

XMLConfigBuilder:

1、XMLConfigBuilder构造函数中会先构建XPathParse对象:

XPathParse类的主要内容:

  • XPathParse类基于java XPath解析器,真正用于解析XML文件元素节点的解析器,XMLConfigBuilder中解析xml的核心。
  • Document,XML解析后生成的document对象(w3c的文档对象)
  • EntityResolver,XML实体解析器
  • Properties,属性变量,用来替换xml中动态配置的属性值
  • Xpath xpath,javax.xml.xpath.XPath对象,主要是用xpath.evaluate查询XML中节点和元素,并返回XNode节点。

2、parse()方法用来生成Configuration配置对象

  • 方法内使用(XPathParser) parser.evalNode("/configuration"),从mybatis-config.xml配置根节点configuration解析,而XPathParser parser.evalNode内部使用了Xpath.evaluate来查询XML中节点和元素,并返回XNode节点对象

3、parseConfiguration()方法根据XNode节点遍历解析设置Configuration对象各项属性

可结合mybatis-config.xml配置文档来看:https://mybatis.org/mybatis-3/zh/configuration.html

// XMLConfigBuilder源码
package org.apache.ibatis.builder.xml;

public class XMLConfigBuilder extends BaseBuilder {
    // 是否解析xml配置
    private boolean parsed;
    // XPathParser对象属性,parser.evalNode可以解析xml制定的节点生成XNode对象
    private final XPathParser parser;
    private String environment;
    private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

    // 构造函数中先创建XPathParse对象
    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);     
   }
   // 构造函数给对应属性设置相应的值
   private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        // new Configuration()初始化默认的Configuration对象
        // 调用父类BaseBuilder的构造函数,设置configuration、typeAliasRegistry、typeHandlerRegistry等属性
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        // 设置Properties到configuration中的属性variables
        this.configuration.setVariables(props);
        // 是否解析XML标志
        this.parsed = false;
        this.environment = environment;
        // 存储XPathParser对象
        this.parser = parser;
    }
    
    // 解析XML配置文件,返回Configuration对象
    public Configuration parse() {
        // 如果XML配置解析了就不需要再parse
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        // 使用XPathParser parser来从XML的根节点configuration开始解析,parser.evalNode()方法实际使用Xpath.evaluate来查询XML中节点和元素
        // 根据parser.evalNode返回的XNode节点来解析将节点对应到Configuration配置对象
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
    //根据XNode节点遍历解析设置Configuration对象各项属性,root为configuration根节点XNode
    private void parseConfiguration(XNode root) {
        try {
            // issue #117 read properties first
            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);
        }
    }   
}

XMLConfigBuilder中属性标签的解析源码:

属性标签的配置文档:https://mybatis.org/mybatis-3/zh/configuration.html#properties

/**

  
  

**/

// XMLConfigBuilder 属性标签的解析源码
package org.apache.ibatis.builder.xml;

// 属性标签的解析
private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    // 读取properties标签下面property的配置name和value,放入Properties defaults中
    // 
    Properties defaults = context.getChildrenAsProperties();
    // 获取properties标签的resource属性值,如
    String resource = context.getStringAttribute("resource");
    // 获取properties标签的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.");
    }
    if (resource != null) {
      // 通过Resources.getResourceAsProperties获取本地属性放入defaults中
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
      // Resources.getUrlAsProperties获取网络资源属性放入defaults中
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    // configuration对象的variables属性用来存在这些动态属性,所以获取所有属性
    Properties vars = configuration.getVariables();
    if (vars != null) {
      // 整合
      defaults.putAll(vars);
    }
    // 重新设置XPathParser对象和configuration对象的Variables
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}

XMLConfigBuilder中typeAliases标签的解析源码:

typeAliases标签官方配置文档:https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases

  • 定义:typeAliases为一种类型别名机制,可以给某个具体的类设置一个别名,这样在使用某个类(java bean)时,就可以不需要包名+类名的方式,直接使用别名;
  • 使用:mapper的参数类型parameterType和resultType结果类型都可以使用typeAliases内配置的别名;
  • 作用:仅用于 XML 配置,意在降低冗余的全限定类名书写;
  • 别名注册器:Configuration下面会有TypeAliasRegistry typeAliasRegistry属性来管理别名
  • TypeAliasRegistry类通过Map> typeAliases私有变量通过key/value的方式存储别名和别名指向的java bean。
/**

    
    // 直接通过直接某个bean的别名
    
    
    // 指定一个包名,会扫描包下面的所有java bean,
    // 如果包下面java bean有注解则使用@Alias注解的值
    // 如果包下面java bean没有注解则使用bean的首字母小写非限定类名来作为它的别名
    

**/

// XMLConfigBuilder typeAliases标签的解析源码
package org.apache.ibatis.builder.xml;

// XMLConfigBuilder中解析typeAliases标签
private void typeAliasesElement(XNode parent) {
  if (parent != null) {
    // 获取typeAliases下面所有子节点遍历
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        // 如果配置是package标签,获取package标签下面到name属性值,即包名
        String typeAliasPackage = child.getStringAttribute("name");
        // configuration.getTypeAliasRegistry()为拿到配置对象的typeAliasRegistry别名注册器的方法
        // registerAliases(typeAliasPackage)为包名批量注册
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } else {
        String alias = child.getStringAttribute("alias");
        String type = child.getStringAttribute("type");
        try {
          Class clazz = Resources.classForName(type);
          if (alias == null) {
            // 没有alias属性值时,获取通过类class.getAnnotation(Alias.class)获取类上面的@Alias注解的值
            // 如果也没有注解则获取类名,class.getSimpleName();
            // 注册别名和class到configuration的typeAliasRegistry属性上面
            typeAliasRegistry.registerAlias(clazz);
          } else {
            typeAliasRegistry.registerAlias(alias, clazz);
          }
        } catch (ClassNotFoundException e) {
          throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
        }
      }
    }
  }
}

TypeAliasRegistry根据包来批量注册别名和单个注册核心源码解析:

// TypeAliasRegistry 别名管理注册源码
package org.apache.ibatis.type;


public void registerAliases(String packageName) {
  // 只指定包名时,传入Object.class表示包下的所有类均在别名注册的考虑范围
  registerAliases(packageName, Object.class);
}

// 包名批量注册源码,最终也是单独注册
// superType 限定要注册的类的来源,只有继承自给定类型的类才能被注册
public void registerAliases(String packageName, Class superType) {
  // 工具类ResolverUtil为类加载器用于定位类路径下指定包下面的必要类
  ResolverUtil> resolverUtil = new ResolverUtil<>();
  // 搜索packageName包下面,继承自给定类型superType的类
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  // 获取已搜索的包下指定条件类集合
  Set>> typeSet = resolverUtil.getClasses();
  for (Class type : typeSet) {
    // Ignore inner classes and interfaces (including package-info.java)
    // Skip also inner classes. See issue #6
    // isAnonymousClass()用于检查基础类是否为匿名类(anonymousClass)
    // isInterface()用于检查此Class是否是接口
    // isMemberClass()用于检查基础类是否为成员类(memberClass)
    // https://blog.csdn.net/idealcitier/article/details/106728539
    // https://www.zhihu.com/question/67270393
    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
      // 排除匿名类、接口、成员类后的类再进行单个注册
      registerAlias(type);
    }
  }
}

// 逐个注册别名核心源码
public void registerAlias(Class type) {
  // getSimplName()获取类的简写名称(不包含包路径)
  // getName()获取得到类名称(包含路径)
  String alias = type.getSimpleName();
  // 获取类上Alias注解
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    // 如果注解值不为null则使用类上注解@Alias("xxx")的值xxx为别名来注册
    alias = aliasAnnotation.value();
  }
  // 没有注解则使用类的SimpleName为别名注册
  registerAlias(alias, type);
}

public void registerAlias(String alias, Class value) {
  if (alias == null) {
    // 别名为null抛错
    throw new TypeException("The parameter alias cannot be null");
  }
  // issue #748
  // 别名转小写
  String key = alias.toLowerCase(Locale.ENGLISH);
  // Map> typeAliases为TypeAliasRegistry中的管理器
  // 如果已经注册过则不重复注册
  if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
    throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
  }
  // 放入Map
  typeAliases.put(key, value);
}

TypeAliasRegistry类的源码方法解析:

构造函数会初始化一些内置类的别名,如:java的基本数据类型等,所以在Mybatis的xml中可以使用一些别名来替代java的基本数据类型,mapper里的parameterType和resultType。

// TypeAliasRegistry
package org.apache.ibatis.type;

public class TypeAliasRegistry {
    // 别名机制管理器Map
    private final Map> typeAliases = new HashMap<>();
  
    // 构造函数,注册一些java内部的类到别名机制管理器上面
    public TypeAliasRegistry() {
      registerAlias("string", String.class);
    
      registerAlias("byte", Byte.class);
      registerAlias("long", Long.class);
      registerAlias("short", Short.class);
      registerAlias("int", Integer.class);
      registerAlias("integer", Integer.class);
      registerAlias("double", Double.class);
      registerAlias("float", Float.class);
      registerAlias("boolean", Boolean.class);
    
      registerAlias("byte[]", Byte[].class);
      registerAlias("long[]", Long[].class);
      registerAlias("short[]", Short[].class);
      registerAlias("int[]", Integer[].class);
      registerAlias("integer[]", Integer[].class);
      registerAlias("double[]", Double[].class);
      registerAlias("float[]", Float[].class);
      registerAlias("boolean[]", Boolean[].class);
    
      registerAlias("_byte", byte.class);
      registerAlias("_long", long.class);
      registerAlias("_short", short.class);
      registerAlias("_int", int.class);
      registerAlias("_integer", int.class);
      registerAlias("_double", double.class);
      registerAlias("_float", float.class);
      registerAlias("_boolean", boolean.class);
    
      registerAlias("_byte[]", byte[].class);
      registerAlias("_long[]", long[].class);
      registerAlias("_short[]", short[].class);
      registerAlias("_int[]", int[].class);
      registerAlias("_integer[]", int[].class);
      registerAlias("_double[]", double[].class);
      registerAlias("_float[]", float[].class);
      registerAlias("_boolean[]", boolean[].class);
    
      registerAlias("date", Date.class);
      registerAlias("decimal", BigDecimal.class);
      registerAlias("bigdecimal", BigDecimal.class);
      registerAlias("biginteger", BigInteger.class);
      registerAlias("object", Object.class);
    
      registerAlias("date[]", Date[].class);
      registerAlias("decimal[]", BigDecimal[].class);
      registerAlias("bigdecimal[]", BigDecimal[].class);
      registerAlias("biginteger[]", BigInteger[].class);
      registerAlias("object[]", Object[].class);
    
      registerAlias("map", Map.class);
      registerAlias("hashmap", HashMap.class);
      registerAlias("list", List.class);
      registerAlias("arraylist", ArrayList.class);
      registerAlias("collection", Collection.class);
      registerAlias("iterator", Iterator.class);
    
      registerAlias("ResultSet", ResultSet.class);
    }
    
    // TypeAliasRegistry中解析别名方法
    public  Class resolveAlias(String string) {
      try {
        if (string == null) {
          return null;
        }
        // issue #748
        String key = string.toLowerCase(Locale.ENGLISH);
        Class value;
        if (typeAliases.containsKey(key)) {
          // 如果typeAliases里面包含key,直接从HashMap中取
          value = (Class) typeAliases.get(key);
        } else {
          // 如果typeAliases里面不包含key,通过Resources.classForName获取类路径对应的Class实例
          // 详情见Resources和ClassLoaderWrapper源码分析
          value = (Class) Resources.classForName(string);
        }
        return value;
      } catch (ClassNotFoundException e) {
        throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
      }
    }
  
    ...
}

XMLConfigBuilder中mappers标签的解析源码:

mappers映射器的配置规则 ,可看官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#mappers

/**

    
    
    
    
    
    
    
    
    

**/

// XMLConfigBuilder mappers标签的解析源码
package org.apache.ibatis.builder.xml;

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 循环便利mappers下面的子标签
    for (XNode child : parent.getChildren()) {
      // 解析package标签配置的包
      if ("package".equals(child.getName())) {
        // 获取包名称
        String mapperPackage = child.getStringAttribute("name");
        // configuration对象中根据包名添加包下面所有类
        // 其中根据resolverUtil.find(new ResolverUtil.IsA(superType), packageName)来获取包下面所有.class类
        // 便利所以class然后addMapper(mapperClass);去添加mapper类接口的动态代理MapperProxyFactory类
        // MapperProxyFactory使用了JDK的Proxy.newProxyInstance代理方法实现动态代理
        // 添加到configuration对象的mapperRegistry中,用来管理所有mapper
        configuration.addMappers(mapperPackage);
      } else {
        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);
          // 外部Mapper.xml生成输入流
          try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
            // 通过XMLMapperBuilder来解析Mapper.xml文件
            // 底层也是使用XPathParser去解析
            // mapperParser.parse()内去解析mapper.xml的sql语句statement,result、parame等
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          try(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
          Class mapperInterface = Resources.classForName(mapperClass);
          // 添加到configuration对象的mapperRegistry中,用来管理所有mapper
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

DTD(Document Type Definition,文档类型定义)

定义 XML 文档的合法构建模块,它使用一系列的合法元素来定义文档结构,官方文档:https://www.w3school.com.cn/dtd/dtd_intro.asp

XSD(XML Schema Definition,XML Schema 语言)

XML Schema 是基于 XML 的 DTD 替代者。也是描述 XML 文档的结构,官方文档:https://www.w3school.com.cn/schema/index.asp

XML配置文件标签使用的DTD声明位置:org.apache.ibatis.builder.xml.mybatis-3-config.dtd

XML配置文件标签使用的XSD声明位置:org.apache.ibatis.builder.xml.mybatis-config.xsd

XMLMapper文件标签使用的DTD声明位置:org.apache.ibatis.builder.xml.mybatis-3-mapper.dtd

XMLMapper文件标签使用的XSD声明位置:org.apache.ibatis.builder.xml.mybatis-mapper.xsd

XMLConfigBuilder中plugins的解析源码:

属性标签的配置文档:https://mybatis.org/mybatis-3/zh/configuration.html#plugins

可以使用插件拦截的方法有:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed),Executor 是负责执行底层映射语句的内部对象
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

// XMLConfigBuilder plugins标签的解析源码
package org.apache.ibatis.builder.xml;

private void pluginElement(XNode parent) throws Exception {
  if (parent != null) {
    // 获取子节点遍历
    for (XNode child : parent.getChildren()) {
      // 获取子节点的interceptor属性配置值,为插件实现类的完全限定类名
      String interceptor = child.getStringAttribute("interceptor");
      // 获取子节点内的属性配置
      Properties properties = child.getChildrenAsProperties();
      // 
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
      interceptorInstance.setProperties(properties);
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

总体流程总结:

  • 解析mybatis-config.xml配置文件,生成对应的输入流
  • 通过SqlSessionFactoryBuilder().build(inputStream)来构建SqlSessionFactory对象
  • SqlSessionFactoryBuilder.build中先对输入流使用XMLConfigBuilder(建造者模式)来创建XMLConfigBuilder实例,来解析xml配置文件,包含了创建XPathParser对象(核心java XPath来解析xml生成XNode节点树),XPathParser对象内创建了Document对象;
  • 通过XMLConfigBuilder实例的parse,parser.parse()来返回Configuration配置对象,parse方法中使用parseConfiguration解析每个标签的内容,并赋值给Configuration配置对象;
  • 调用创建DefaultSqlSessionFactory对象的build方法生产最终需要的SqlSessionFactory。

你可能感兴趣的:(Mybatis系列,mybatis)