# 精尽 MyBatis 源码分析 —— MyBatis 初始化(一)之加载 mybatis-config # 1. 概述 从本文开始,我们来分享 MyBatis 初始化的流程。在 [《精尽 MyBatis 源码分析 —— 项目结构一览》](http://svip.iocoder.cn/MyBatis/intro) 中,我们简单介绍这个流程如下: > 在 MyBatis 初始化过程中,会加载 `mybatis-config.xml` 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configuration 对象中。例如: > > - ``节点(即 ResultSet 的映射规则) 会被解析成 ResultMap 对象。 > - `` 节点(即属性映射)会被解析成 ResultMapping 对象。 > > 之后,利用该 Configuration 对象创建 SqlSessionFactory对象。待 MyBatis 初始化之后,开发人员可以通过初始化得到 SqlSessionFactory 创建 SqlSession 对象并完成数据库操作。 - 对应 `builder` 模块,为配置**解析过程** - 对应 `mapping` 模块,主要为 SQL 操作解析后的**映射**。 因为整个 MyBatis 的初始化流程涉及代码颇多,所以拆分成三篇文章: - 加载 `mybatis-config.xml` 配置文件。 - 加载 Mapper 映射配置文件。 - 加载 Mapper 接口中的注解信息。 本文就主要分享第一部分「加载 `mybatis-config.xml` 配置文件」。 ------ MyBatis 的初始化流程的**入口**是 SqlSessionFactoryBuilder 的 `#build(Reader reader, String environment, Properties properties)` 方法,代码如下: > SqlSessionFactoryBuilder 中,build 方法有多种重载方式。这里就选取一个。 ``` // SqlSessionFactoryBuilder.java /** * 构造 SqlSessionFactory 对象 * * @param reader Reader 对象 * @param environment 环境 * @param properties Properties 变量 * @return SqlSessionFactory 对象 */ public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { // <1> 创建 XMLConfigBuilder 对象 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); // <2> 执行 XML 解析 // <3> 创建 DefaultSqlSessionFactory 对象 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. } } } ``` - `<1>` 处,创建 XMLConfigBuilder 对象。 - `<2>` 处,调用 `XMLConfigBuilder#parse()` 方法,执行 XML 解析,返回 Configuration 对象。 - `<3>` 处,创建 DefaultSqlSessionFactory 对象。 - 本文的重点是 `<1>` 和 `<2>` 处,即 XMLConfigBuilder 类。详细解析,见 [「3. XMLConfigBuilder」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 # 2. BaseBuilder `org.apache.ibatis.builder.BaseBuilder` ,基础构造器抽象类,为子类提供通用的工具类。 为什么不直接讲 XMLConfigBuilder ,而是先讲 BaseBuilder 呢?因为,BaseBuilder 是 XMLConfigBuilder 的父类,并且它还有其他的子类。如下图所示:[![BaseBuilder 类图](15-mybatis-MyBatis 初始化(一)之加载 mybatis-config.assets/01.png)](http://static.iocoder.cn/images/MyBatis/2020_02_10/01.png)BaseBuilder 类图 ## 2.1 构造方法 ``` // BaseBuilder.java /** * MyBatis Configuration 对象 */ protected final Configuration configuration; protected final TypeAliasRegistry typeAliasRegistry; protected final TypeHandlerRegistry typeHandlerRegistry; public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); } ``` - `configuration` 属性,MyBatis Configuration 对象。XML 和注解中解析到的配置,最终都会设置到 [`org.apache.ibatis.session.Configuration`](https://github.com/YunaiV/mybatis-3/blob/master/src/main/java/org/apache/ibatis/session/Configuration.java) 中。感兴趣的胖友,可以先点击瞅一眼。抽完之后,马上回来。 ## 2.2 parseExpression `#parseExpression(String regex, String defaultValue)` 方法,创建正则表达式。代码如下: ``` // BaseBuilder.java /** * 创建正则表达式 * * @param regex 指定表达式 * @param defaultValue 默认表达式 * @return 正则表达式 */ @SuppressWarnings("SameParameterValue") protected Pattern parseExpression(String regex, String defaultValue) { return Pattern.compile(regex == null ? defaultValue : regex); } ``` ## 2.3 xxxValueOf `#xxxValueOf(...)` 方法,将字符串转换成对应的数据类型的值。代码如下: ``` // BaseBuilder.java protected Boolean booleanValueOf(String value, Boolean defaultValue) { return value == null ? defaultValue : Boolean.valueOf(value); } protected Integer integerValueOf(String value, Integer defaultValue) { return value == null ? defaultValue : Integer.valueOf(value); } protected Set stringSetValueOf(String value, String defaultValue) { value = (value == null ? defaultValue : value); return new HashSet<>(Arrays.asList(value.split(","))); } ``` ## 2.4 resolveJdbcType `#resolveJdbcType(String alias)` 方法,解析对应的 JdbcType 类型。代码如下: ``` // BaseBuilder.java protected JdbcType resolveJdbcType(String alias) { if (alias == null) { return null; } try { return JdbcType.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving JdbcType. Cause: " + e, e); } } ``` ## 2.5 resolveResultSetType `#resolveResultSetType(String alias)` 方法,解析对应的 ResultSetType 类型。代码如下: ``` // BaseBuilder.java protected ResultSetType resolveResultSetType(String alias) { if (alias == null) { return null; } try { return ResultSetType.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e); } } ``` ## 2.6 resolveParameterMode `#resolveParameterMode(String alias)` 方法,解析对应的 ParameterMode 类型。代码如下: ``` // BaseBuilder.java protected ParameterMode resolveParameterMode(String alias) { if (alias == null) { return null; } try { return ParameterMode.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e); } } ``` ## 2.7 createInstance `#createInstance(String alias)` 方法,创建指定对象。代码如下: ``` // BaseBuilder.java protected Object createInstance(String alias) { // <1> 获得对应的类型 Class clazz = resolveClass(alias); if (clazz == null) { return null; } try { // <2> 创建对象 return resolveClass(alias).newInstance(); // 这里重复获得了一次 } catch (Exception e) { throw new BuilderException("Error creating instance. Cause: " + e, e); } } ``` - `<1>` 处,调用 `#resolveClass(String alias)` 方法,获得对应的类型。代码如下: ``` // BaseBuilder.java protected Class resolveClass(String alias) { if (alias == null) { return null; } try { return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); } } protected Class resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias); } ``` - 从 `typeAliasRegistry` 中,通过别名或类全名,获得对应的类。 - `<2>` 处,创建对象。 ## 2.8 resolveTypeHandler `#resolveTypeHandler(Class javaType, String typeHandlerAlias)` 方法,从 `typeHandlerRegistry` 中获得或创建对应的 TypeHandler 对象。代码如下: ``` // BaseBuilder.java protected TypeHandler resolveTypeHandler(Class javaType, Class> typeHandlerType) { if (typeHandlerType == null) { return null; } // javaType ignored for injected handlers see issue #746 for full detail // 先获得 TypeHandler 对象 TypeHandler handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType); if (handler == null) { // 如果不存在,进行创建 TypeHandler 对象 // not in registry, create a new one handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType); } return handler; } ``` # 3. XMLConfigBuilder `org.apache.ibatis.builder.xml.XMLConfigBuilder` ,继承 BaseBuilder 抽象类,XML 配置构建器,主要负责解析 mybatis-config.xml 配置文件。即对应 [《MyBatis 文档 —— XML 映射配置文件》](http://www.mybatis.org/mybatis-3/zh/configuration.html) 。 ## 3.1 构造方法 ``` // XMLConfigBuilder.java /** * 是否已解析 */ private boolean parsed; /** * 基于 Java XPath 解析器 */ private final XPathParser parser; /** * 环境 */ private String environment; /** * ReflectorFactory 对象 */ private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); public XMLConfigBuilder(Reader reader) { this(reader, null, null); } public XMLConfigBuilder(Reader reader, String environment) { this(reader, environment, null); } public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } public XMLConfigBuilder(InputStream inputStream) { this(inputStream, null, null); } public XMLConfigBuilder(InputStream inputStream, String environment) { this(inputStream, environment, null); } 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) { // <1> 创建 Configuration 对象 super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); // <2> 设置 Configuration 的 variables 属性 this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } ``` - `parser` 属性,XPathParser 对象。在 [《精尽 MyBatis 源码分析 —— 解析器模块》](http://svip.iocoder.cn/MyBatis/parsing-package) 中,已经详细解析。 - `localReflectorFactory` 属性,DefaultReflectorFactory 对象。在 [《精尽 MyBatis 源码分析 —— 反射模块》](http://svip.iocoder.cn/MyBatis/reflection-package) 中,已经详细解析。 - 构造方法重载了比较多,只需要看最后一个。 - `<1>` 处,创建 Configuration 对象。 - `<2>` 处,设置 Configuration 对象的 `variables` 属性。代码如下: ``` // Configuration.java /** * 变量 Properties 对象。 * * 参见 {@link org.apache.ibatis.builder.xml.XMLConfigBuilder#propertiesElement(XNode context)} 方法 */ protected Properties variables = new Properties(); public void setVariables(Properties variables) { this.variables = variables; } ``` ## 3.2 parse `#parse()` 方法,解析 XML 成 Configuration 对象。代码如下: ``` // XMLConfigBuilder.java public Configuration parse() { // <1.1> 若已解析,抛出 BuilderException 异常 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } // <1.2> 标记已解析 parsed = true; // <2> 解析 XML configuration 节点 parseConfiguration(parser.evalNode("/configuration")); return configuration; } ``` - `<1.1>` 处,若已解析,抛出 BuilderException 异常。 - `<1.2>` 处,标记已解析。 - `<2>` 处,调用 `XPathParser#evalNode(String expression)` 方法,获得 XML `` 节点,后调用 `#parseConfiguration(XNode root)` 方法,解析该节点。详细解析,见 [「3.3 parseConfiguration」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 ## 3.3 parseConfiguration `#parseConfiguration(XNode root)` 方法,解析 `` 节点。代码如下: ``` // XMLConfigBuilder.java private void parseConfiguration(XNode root) { try { //issue #117 read properties first // <1> 解析 标签 propertiesElement(root.evalNode("properties")); // <2> 解析 标签 Properties settings = settingsAsProperties(root.evalNode("settings")); // <3> 加载自定义 VFS 实现类 loadCustomVfs(settings); // <4> 解析 标签 typeAliasesElement(root.evalNode("typeAliases")); // <5> 解析 标签 pluginElement(root.evalNode("plugins")); // <6> 解析 标签 objectFactoryElement(root.evalNode("objectFactory")); // <7> 解析 标签 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // <8> 解析 标签 reflectorFactoryElement(root.evalNode("reflectorFactory")); // <9> 赋值 到 Configuration 属性 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // <10> 解析 标签 environmentsElement(root.evalNode("environments")); // <11> 解析 标签 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // <12> 解析 标签 typeHandlerElement(root.evalNode("typeHandlers")); // <13> 解析 标签 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } ``` - `<1>` 处,调用 `#propertiesElement(XNode context)` 方法,解析 `` 节点。详细解析,见 [「3.3.1 propertiesElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<2>` 处,调用 `#settingsAsProperties(XNode context)` 方法,解析 `` 节点。详细解析,见 [「3.3.2 settingsAsProperties」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<3>` 处,调用 `#loadCustomVfs(Properties settings)` 方法,加载自定义 VFS 实现类。详细解析,见 [「3.3.3 loadCustomVfs」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<4>` 处,调用 `#typeAliasesElement(XNode parent)` 方法,解析 `` 节点。详细解析,见 [「3.3.4 typeAliasesElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<5>` 处,调用 `#typeAliasesElement(XNode parent)` 方法,解析 `` 节点。详细解析,见 [「3.3.5 pluginElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<6>` 处,调用 `#objectFactoryElement(XNode parent)` 方法,解析 `` 节点。详细解析,见 [「3.3.6 pluginElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<7>` 处,调用 `#objectWrapperFactoryElement(XNode parent)` 方法,解析 `` 节点。详细解析,见 [「3.3.7 objectWrapperFactoryElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<8>` 处,调用 `#reflectorFactoryElement(XNode parent)` 方法,解析 `` 节点。详细解析,见 [「3.3.8 reflectorFactoryElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<9>` 处,调用 `#settingsElement(Properties props)` 方法,赋值 `` 到 Configuration 属性。详细解析,见 [「3.3.9 settingsElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<10>` 处,调用 `#environmentsElement(XNode context)` 方法,解析 `` 标签。详细解析,见 [「3.3.10 environmentsElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<11>` 处,调用 `#databaseIdProviderElement(XNode context)` 方法,解析 `` 标签。详细解析,见 [「3.3.11 databaseIdProviderElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<12>` 处,调用 `#typeHandlerElement(XNode context)` 方法,解析 `` 标签。详细解析,见 [「3.3.12 typeHandlerElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 - `<13>` 处,调用 `#mapperElement(XNode context)` 方法,解析 `` 标签。详细解析,见 [「3.3.13 mapperElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。 ### 3.3.1 propertiesElement `#propertiesElement(XNode context)` 方法,解析 `` 节点。大体逻辑如下: 1. 解析 `` 标签,成 Properties 对象。 2. 覆盖 `configuration` 中的 Properties 对象到上面的结果。 3. 设置结果到 `parser` 和 `configuration` 中。 代码如下: ``` // XMLConfigBuilder.java private void propertiesElement(XNode context) throws Exception { if (context != null) { // 读取子标签们,为 Properties 对象 Properties defaults = context.getChildrenAsProperties(); // 读取 resource 和 url 属性 String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { // resource 和 url 都存在的情况下,抛出 BuilderException 异常 throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } // 读取本地 Properties 配置文件到 defaults 中。 if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); // 读取远程 Properties 配置文件到 defaults 中。 } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } // 覆盖 configuration 中的 Properties 对象到 defaults 中。 Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } // 设置 defaults 到 parser 和 configuration 中。 parser.setVariables(defaults); configuration.setVariables(defaults); } } ``` ### 3.3.2 settingsAsProperties `#settingsElement(Properties props)` 方法,将 `` 标签解析为 Properties 对象。代码如下: ``` // XMLConfigBuilder.java private Properties settingsAsProperties(XNode context) { // 将子标签,解析成 Properties 对象 if (context == null) { return new Properties(); } Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class // 校验每个属性,在 Configuration 中,有相应的 setting 方法,否则抛出 BuilderException 异常 MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; } ``` ### 3.3.3 loadCustomVfs `#loadCustomVfs(Properties settings)` 方法,加载自定义 VFS 实现类。代码如下: ``` // XMLConfigBuilder.java private void loadCustomVfs(Properties props) throws ClassNotFoundException { // 获得 vfsImpl 属性 String value = props.getProperty("vfsImpl"); if (value != null) { // 使用 , 作为分隔符,拆成 VFS 类名的数组 String[] clazzes = value.split(","); // 遍历 VFS 类名的数组 for (String clazz : clazzes) { if (!clazz.isEmpty()) { // 获得 VFS 类 @SuppressWarnings("unchecked") Class vfsImpl = (Class) Resources.classForName(clazz); // 设置到 Configuration 中 configuration.setVfsImpl(vfsImpl); } } } } // Configuration.java /** * VFS 实现类 */ protected Class vfsImpl; public void setVfsImpl(Class vfsImpl) { if (vfsImpl != null) { // 设置 vfsImpl 属性 this.vfsImpl = vfsImpl; // 添加到 VFS 中的自定义 VFS 类的集合 VFS.addImplClass(this.vfsImpl); } } ``` ### 3.3.4 typeAliasesElement `#typeAliasesElement(XNode parent)` 方法,解析 `` 标签,将配置类注册到 `typeAliasRegistry` 中。代码如下: ``` // XMLConfigBuilder.java private void typeAliasesElement(XNode parent) { if (parent != null) { // 遍历子节点 for (XNode child : parent.getChildren()) { // 指定为包的情况下,注册包下的每个类 if ("package".equals(child.getName())) { String typeAliasPackage = child.getStringAttribute("name"); configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); // 指定为类的情况下,直接注册类和别名 } else { String alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); try { Class clazz = Resources.classForName(type); // 获得类是否存在 // 注册到 typeAliasRegistry 中 if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { // 若类不存在,则抛出 BuilderException 异常 throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } } } ``` ### 3.3.5 pluginElement `#pluginElement(XNode parent)` 方法,解析 `` 标签,添加到 `Configuration#interceptorChain` 中。代码如下: ``` // XMLConfigBuilder.java private void pluginElement(XNode parent) throws Exception { if (parent != null) { // 遍历 标签 for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); // <1> 创建 Interceptor 对象,并设置属性 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); // <2> 添加到 configuration 中 configuration.addInterceptor(interceptorInstance); } } } ``` - `<1>` 处,创建 Interceptor 对象,并设置属性。关于 Interceptor 类,后续文章,详细解析。 - `<2>` 处,调用 `Configuration#addInterceptor(Interceptor interceptor)` 方法,添加到 `configuration` 中。代码如下: ``` // Configuration.java /** * 拦截器链 */ protected final InterceptorChain interceptorChain = new InterceptorChain(); public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); } ``` - 关于 InterceptorChain 类,后续文章,详细解析。 ### 3.3.6 objectFactoryElement `#objectFactoryElement(XNode parent)` 方法,解析 `` 节点。代码如下: ``` // XMLConfigBuilder.java private void objectFactoryElement(XNode context) throws Exception { if (context != null) { // 获得 ObjectFactory 的实现类 String type = context.getStringAttribute("type"); // 获得 Properties 属性 Properties properties = context.getChildrenAsProperties(); // <1> 创建 ObjectFactory 对象,并设置 Properties 属性 ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance(); factory.setProperties(properties); // <2> 设置 Configuration 的 objectFactory 属性 configuration.setObjectFactory(factory); } } ``` - `<1>` 处,创建 ObjectFactory 对象,并设置 Properties 属性。 - `<2>` 处,调用 `Configuration#setObjectFactory(ObjectFactory objectFactory)` 方法,设置 Configuration 的 `objectFactory` 属性。代码如下: ``` // Configuration.java /** * ObjectFactory 对象 */ protected ObjectFactory objectFactory = new DefaultObjectFactory(); public void setObjectFactory(ObjectFactory objectFactory) { this.objectFactory = objectFactory; } ``` ### 3.3.7 objectWrapperFactoryElement `#objectWrapperFactoryElement(XNode context)` 方法,解析 `` 节点。代码如下: ``` // XMLConfigBuilder.java private void objectWrapperFactoryElement(XNode context) throws Exception { if (context != null) { // 获得 ObjectFactory 的实现类 String type = context.getStringAttribute("type"); // <1> 创建 ObjectWrapperFactory 对象 ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance(); // 设置 Configuration 的 objectWrapperFactory 属性 configuration.setObjectWrapperFactory(factory); } } ``` - `<1>` 处,创建 ObjectWrapperFactory 对象。 - `<2>` 处,调用 `Configuration#setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory)` 方法,设置 Configuration 的 `objectWrapperFactory` 属性。代码如下: ``` // Configuration.java /** * ObjectWrapperFactory 对象 */ protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory(); public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) { this.objectWrapperFactory = objectWrapperFactory; } ``` ### 3.3.8 reflectorFactoryElement `#reflectorFactoryElement(XNode parent)` 方法,解析 `` 节点。代码如下: ``` // XMLConfigBuilder.java private void reflectorFactoryElement(XNode context) throws Exception { if (context != null) { // 获得 ReflectorFactory 的实现类 String type = context.getStringAttribute("type"); // 创建 ReflectorFactory 对象 ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance(); // 设置 Configuration 的 reflectorFactory 属性 configuration.setReflectorFactory(factory); } } ``` - `<1>` 处,创建 ReflectorFactory 对象。 - `<2>` 处,调用 `Configuration#setReflectorFactory(ReflectorFactory reflectorFactory)` 方法,设置 Configuration 的 `reflectorFactory` 属性。代码如下: ``` // Configuration.java /** * ReflectorFactory 对象 */ protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); public void setReflectorFactory(ReflectorFactory reflectorFactory) { this.reflectorFactory = reflectorFactory; } ``` ### 3.3.9 settingsElement `#settingsElement(Properties props)` 方法,赋值 `` 到 Configuration 属性。代码如下: ``` // XMLConfigBuilder.java private void settingsElement(Properties props) throws Exception { configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); @SuppressWarnings("unchecked") Class typeHandler = resolveClass(props.getProperty("defaultEnumTypeHandler")); configuration.setDefaultEnumTypeHandler(typeHandler); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); @SuppressWarnings("unchecked") Class logImpl = resolveClass(props.getProperty("logImpl")); configuration.setLogImpl(logImpl); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); } ``` - 属性比较多,瞟一眼就行。 ### 3.3.10 environmentsElement `#environmentsElement(XNode context)` 方法,解析 `` 标签。代码如下: ``` // XMLConfigBuilder.java private void environmentsElement(XNode context) throws Exception { if (context != null) { // <1> environment 属性非空,从 default 属性获得 if (environment == null) { environment = context.getStringAttribute("default"); } // 遍历 XNode 节点 for (XNode child : context.getChildren()) { // <2> 判断 environment 是否匹配 String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { // <3> 解析 `` 标签,返回 TransactionFactory 对象 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); // <4> 解析 `` 标签,返回 DataSourceFactory 对象 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); // <5> 创建 Environment.Builder 对象 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); // <6> 构造 Environment 对象,并设置到 configuration 中 configuration.setEnvironment(environmentBuilder.build()); } } } } ``` - `<1>` 处,若 `environment` 属性非空,从 `default` 属性种获得 `environment` 属性。 - `<2>` 处,遍历 XNode 节点,获得其 `id` 属性,后调用 `#isSpecifiedEnvironment(String id)` 方法,判断 `environment` 和 `id` 是否匹配。代码如下: ``` // XMLConfigBuilder.java private boolean isSpecifiedEnvironment(String id) { if (environment == null) { throw new BuilderException("No environment specified."); } else if (id == null) { throw new BuilderException("Environment requires an id attribute."); } else if (environment.equals(id)) { // 相等 return true; } return false; } ``` - `<3>` 处,调用 `#transactionManagerElement(XNode context)` 方法,解析 `` 标签,返回 TransactionFactory 对象。代码如下: ``` // XMLConfigBuilder.java private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { // 获得 TransactionFactory 的类 String type = context.getStringAttribute("type"); // 获得 Properties 属性 Properties props = context.getChildrenAsProperties(); // 创建 TransactionFactory 对象,并设置属性 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); } ``` - `<4>` 处,调用 `#dataSourceElement(XNode context)` 方法,解析 `` 标签,返回 DataSourceFactory 对象,而后返回 DataSource 对象。代码如下: ``` // XMLConfigBuilder.java private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { // 获得 DataSourceFactory 的类 String type = context.getStringAttribute("type"); // 获得 Properties 属性 Properties props = context.getChildrenAsProperties(); // 创建 DataSourceFactory 对象,并设置属性 DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); } ``` - `<5>` 处,创建 Environment.Builder 对象。 - `<6>` 处,构造 Environment 对象,并设置到 `configuration` 中。代码如下: ``` // Configuration.java /** * DB Environment 对象 */ protected Environment environment; public void setEnvironment(Environment environment) { this.environment = environment; } ``` #### 3.3.10.1 Environment `org.apache.ibatis.mapping.Environment` ,DB 环境。代码如下: ``` // Environment.java public final class Environment { /** * 环境变好 */ private final String id; /** * TransactionFactory 对象 */ private final TransactionFactory transactionFactory; /** * DataSource 对象 */ private final DataSource dataSource; public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) { if (id == null) { throw new IllegalArgumentException("Parameter 'id' must not be null"); } if (transactionFactory == null) { throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null"); } this.id = id; if (dataSource == null) { throw new IllegalArgumentException("Parameter 'dataSource' must not be null"); } this.transactionFactory = transactionFactory; this.dataSource = dataSource; } /** * 构造器 */ public static class Builder { /** * 环境变好 */ private String id; /** * TransactionFactory 对象 */ private TransactionFactory transactionFactory; /** * DataSource 对象 */ private DataSource dataSource; public Builder(String id) { this.id = id; } public Builder transactionFactory(TransactionFactory transactionFactory) { this.transactionFactory = transactionFactory; return this; } public Builder dataSource(DataSource dataSource) { this.dataSource = dataSource; return this; } public String id() { return this.id; } public Environment build() { return new Environment(this.id, this.transactionFactory, this.dataSource); } } public String getId() { return this.id; } public TransactionFactory getTransactionFactory() { return this.transactionFactory; } public DataSource getDataSource() { return this.dataSource; } } ``` ### 3.3.11 databaseIdProviderElement `#databaseIdProviderElement(XNode context)` 方法,解析 `` 标签。代码如下: ``` // XMLConfigBuilder.java private void databaseIdProviderElement(XNode context) throws Exception { DatabaseIdProvider databaseIdProvider = null; if (context != null) { // <1> 获得 DatabaseIdProvider 的类 String type = context.getStringAttribute("type"); // awful patch to keep backward compatibility 保持兼容 if ("VENDOR".equals(type)) { type = "DB_VENDOR"; } // <2> 获得 Properties 对象 Properties properties = context.getChildrenAsProperties(); // <3> 创建 DatabaseIdProvider 对象,并设置对应的属性 databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance(); databaseIdProvider.setProperties(properties); } Environment environment = configuration.getEnvironment(); if (environment != null && databaseIdProvider != null) { // <4> 获得对应的 databaseId 编号 String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); // <5> 设置到 configuration 中 configuration.setDatabaseId(databaseId); } } ``` - 不了解的胖友,可以先看看 [《MyBatis 文档 —— XML 映射配置文件 —— databaseIdProvider 数据库厂商标识》](http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider) 。 - `<1>` 处,获得 DatabaseIdProvider 的类。 - `<2>` 处,获得 Properties 对象。 - `<3>` 处,创建 DatabaseIdProvider 对象,并设置对应的属性。 - `<4>` 处,调用 `DatabaseIdProvider#getDatabaseId(DataSource dataSource)` 方法,获得对应的 `databaseId` **标识**。 - `<5>` 处,设置到 `configuration` 中。代码如下: ``` // Configuration.java /** * 数据库标识 */ protected String databaseId; public void setDatabaseId(String databaseId) { this.databaseId = databaseId; } ``` #### 3.3.11.1 DatabaseIdProvider `org.apache.ibatis.mapping.DatabaseIdProvider` ,数据库标识提供器接口。代码如下: ``` public interface DatabaseIdProvider { /** * 设置属性 * * @param p Properties 对象 */ void setProperties(Properties p); /** * 获得数据库标识 * * @param dataSource 数据源 * @return 数据库标识 * @throws SQLException 当 DB 发生异常时 */ String getDatabaseId(DataSource dataSource) throws SQLException; } ``` #### 3.3.11.2 VendorDatabaseIdProvider `org.apache.ibatis.mapping.VendorDatabaseIdProvider` ,实现 DatabaseIdProvider 接口,供应商数据库标识提供器实现类。 **① 构造方法** ``` // VendorDatabaseIdProvider.java /** * Properties 对象 */ private Properties properties; @Override public String getDatabaseId(DataSource dataSource) { if (dataSource == null) { throw new NullPointerException("dataSource cannot be null"); } try { return getDatabaseName(dataSource); } catch (Exception e) { log.error("Could not get a databaseId from dataSource", e); } return null; } @Override public void setProperties(Properties p) { this.properties = p; } ``` **② 获得数据库标识** `#getDatabaseId(DataSource dataSource)` 方法,代码如下: ``` // VendorDatabaseIdProvider.java @Override public String getDatabaseId(DataSource dataSource) { if (dataSource == null) { throw new NullPointerException("dataSource cannot be null"); } try { // 获得数据库标识 return getDatabaseName(dataSource); } catch (Exception e) { log.error("Could not get a databaseId from dataSource", e); } return null; } private String getDatabaseName(DataSource dataSource) throws SQLException { // <1> 获得数据库产品名 String productName = getDatabaseProductName(dataSource); if (this.properties != null) { for (Map.Entry property : properties.entrySet()) { // 如果产品名包含 KEY ,则返回对应的 VALUE if (productName.contains((String) property.getKey())) { return (String) property.getValue(); } } // no match, return null return null; } // <3> 不存在 properties ,则直接返回 productName return productName; } ``` - `<1>` 处,调用 `#getDatabaseProductName(DataSource dataSource)` 方法,获得数据库产品名。代码如下: ``` // VendorDatabaseIdProvider.java private String getDatabaseProductName(DataSource dataSource) throws SQLException { try (Connection con = dataSource.getConnection()) { // 获得数据库连接 DatabaseMetaData metaData = con.getMetaData(); // 获得数据库产品名 return metaData.getDatabaseProductName(); } } ``` - 通过从 Connection 获得数据库产品名。 - `<2>` 处,如果 `properties` 非空,则从 `properties` 中匹配 `KEY` ?若成功,则返回 `VALUE` ,否则,返回 `null` 。 - `<3>` 处,如果 `properties` 为空,则直接返回 `productName` 。 ### 3.3.12 typeHandlerElement `#typeHandlerElement(XNode parent)` 方法,解析 `` 标签。代码如下: ``` // XMLConfigBuilder.java private void typeHandlerElement(XNode parent) throws Exception { if (parent != null) { // 遍历子节点 for (XNode child : parent.getChildren()) { // <1> 如果是 package 标签,则扫描该包 if ("package".equals(child.getName())) { String typeHandlerPackage = child.getStringAttribute("name"); typeHandlerRegistry.register(typeHandlerPackage); // <2> 如果是 typeHandler 标签,则注册该 typeHandler 信息 } else { // 获得 javaType、jdbcType、handler String javaTypeName = child.getStringAttribute("javaType"); String jdbcTypeName = child.getStringAttribute("jdbcType"); String handlerTypeName = child.getStringAttribute("handler"); Class javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class typeHandlerClass = resolveClass(handlerTypeName); // 非空 // 注册 typeHandler if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } } } ``` - 遍历子节点,分别处理 `<1>` 是 `` 和 `<2>` 是 `` 两种标签的情况。逻辑比较简单,最终都是注册到 `typeHandlerRegistry` 中。 ### 3.3.13 mapperElement `#mapperElement(XNode context)` 方法,解析 `` 标签。代码如下: ``` // XMLConfigBuilder.java private void mapperElement(XNode parent) throws Exception { if (parent != null) { // <0> 遍历子节点 for (XNode child : parent.getChildren()) { // <1> 如果是 package 标签,则扫描该包 if ("package".equals(child.getName())) { // 获得包名 String mapperPackage = child.getStringAttribute("name"); // 添加到 configuration 中 configuration.addMappers(mapperPackage); // 如果是 mapper 标签, } else { // 获得 resource、url、class 属性 String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // <2> 使用相对于类路径的资源引用 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); // 获得 resource 的 InputStream 对象 InputStream inputStream = Resources.getResourceAsStream(resource); // 创建 XMLMapperBuilder 对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 执行解析 mapperParser.parse(); // <3> 使用完全限定资源定位符(URL) } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); // 获得 url 的 InputStream 对象 InputStream inputStream = Resources.getUrlAsStream(url); // 创建 XMLMapperBuilder 对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); // 执行解析 mapperParser.parse(); // <4> 使用映射器接口实现类的完全限定类名 } else if (resource == null && url == null && mapperClass != null) { // 获得 Mapper 接口 Class mapperInterface = Resources.classForName(mapperClass); // 添加到 configuration 中 configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } } ``` - `<0>` 处,遍历子节点,处理每一个节点。根据节点情况,会分成 `<1>`、`<2>`、`<3>`、`<4>` 种情况,并且第一个是处理 `` 标签,后三个是处理 `` 标签。 - `<1>` 处,如果是 `` 标签,则获得 `name` 报名,并调用 `Configuration#addMappers(String packageName)` 方法,扫描该包下的所有 Mapper 接口。代码如下: ``` // Configuration.java /** * MapperRegistry 对象 */ protected final MapperRegistry mapperRegistry = new MapperRegistry(this); public void addMappers(String packageName) { // 扫描该包下所有的 Mapper 接口,并添加到 mapperRegistry 中 mapperRegistry.addMappers(packageName); } ``` - `<4>` 处,如果是 `mapperClass` 非空,则是使用映射器接口实现类的完全限定类名,则获得 Mapper 接口,并调用 `Configuration#addMapper(Class type)` 方法,直接添加到 `configuration` 中。代码如下: ``` // Configuration.java public void addMapper(Class type) { mapperRegistry.addMapper(type); } ``` - 实际上,`<1>` 和 `<4>` 是相似情况,差别在于前者需要扫描,才能获取到所有的 Mapper 接口,而后者明确知道是哪个 Mapper 接口。 - `<2>` 处,如果是 `resource` 非空,则是使用相对于类路径的资源引用,则需要创建 XMLMapperBuilder 对象,并调用 `XMLMapperBuilder#parse()` 方法,执行解析 Mapper XML 配置。执行之后,我们就能知道这个 Mapper XML 配置对应的 Mapper 接口。关于 XMLMapperBuilder 类,我们放在下一篇博客中,详细解析。 - `<3>` 处,如果是 `url` 非空,则是使用完全限定资源定位符(URL),情况和 `<2>` 是类似的。 # 666. 彩蛋 一篇体力活的文章,有点无趣哈。 参考和推荐如下文章: - 祖大俊 [《Mybatis3.3.x技术内幕(八):Mybatis初始化流程(上)》](https://my.oschina.net/zudajun/blog/668738) - 田小波 [《MyBatis 源码分析 - 配置文件解析过程》](https://www.tianxiaobo.com/2018/07/20/MyBatis-源码分析-配置文件解析过程/) - 无忌 [《MyBatis 源码解读之配置》](https://my.oschina.net/wenjinglian/blog/1833051) - 徐郡明 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 的 [「3.1 MyBatis 初始化」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 小节