MyBatis源码解析系列(二)--从SqlSessionFactory诞生说起
看过此篇,你就赚到了。别人写的源码系列,虽然有源码,但是没有关键的图示和debug过程,只是讲了有啥用,不适用。在我写的MyBatis源码系列中,都会结合debug过程+图示来阐述,我们从SqlSessionFactoryBuilder说起。SqlSessionFactoryBuilder,见名知意,是SqlSessionFactory的建造者(Builder)。那么我们猜想,既然是建造SqlSessionFactory,如果让我去写,那么总需要提供一个全参数的建造方法和一些特定参数的建造方法。我们的猜想对与否?验证一下。
一、SqlSessionFactoryBuilder源码解析
在SqlSessionFactoryBuilder类中,我们可以看到如下的构造SqlSessionFactory的方法build(我们这里只说包含Reader的方法,InputStream分析方法类似):
public SqlSessionFactory build(Reader reader) {
return this.build((Reader)reader, (String)null, (Properties)null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return this.build((Reader)reader, environment, (Properties)null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return this.build((Reader)reader, (String)null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
;
}
}
return var5;
}果不其然,跟我们的猜想一样,SqlSessionFactoryBuilder提供了构造SqlSessionFactory全量参数方法public SqlSessionFactory build(Reader reader, String environment, Properties properties),也提供了根据不同特殊要求提供的构造SqlSessionFactory的方法。其实,这一点都不需要惊讶。如果你们研究过其他框架的源码,就知道,这是一种很有效的方式,在jdk的源码中,很多的设计思路跟它一模一样,此处大家可以借鉴。
那我们按照总分的路线去深入研究build(建造)SqlSessionFactory 的方法,那就从全量参数方法开始整。
在全量参数方法build中,我们需要关注的只有两行:
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); var5 = this.build(parser.parse());
①XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
reader是一个文件流读取器,这是将文件转换为流后进行数据配置节点读取的。我们在解析xml中常用到。
environment是一个环境参数,MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,这是用来实现多数据源配置使用的,默认情况下,使用默认default。在解析<environments>元素时就会看到。
properties作为可选参数,是用来进行属性覆盖的。如果我们想覆盖xml中<properties>中配置的某些属性配置,这个参数会用到。口说无凭,在解析<properties>标签内容时候,我们会看到。
②var5 = this.build(parser.parse());
这行是根据xml配置文件的流读取器、使用的环境、设置的属性来得到XMLConfigBuilder,它的作用是什么?就是用来执行解析任务--解析我们篇一中db-core.xml中的内容。
二、XMLConfigBuilder(reader, environment, properties)解密
XMLConfigBuilder的构造函数如下(我们这里只分析使用Reader的构造函数,使用InputStream的同理):
public XMLConfigBuilder(Reader reader) {
this((Reader)reader, (String)null, (Properties)null);
}
public XMLConfigBuilder(Reader reader, String environment) {
this((Reader)reader, environment, (Properties)null);
}
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}在XMLConfigBuilder中,我们也直接看最长的全参数的构造函数:
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}在这里,又构造了XPathParser,我们看下它的构造函数:
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
this.commonConstructor(validation, variables, entityResolver);
this.document = this.createDocument(new InputSource(reader));
}这里四个参数分别用在了两个方法中(这两方法都在XPathParser类中),我们分别看一下:
①commonConstructor方法:
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}在commonConstructor方法中,将构造出的实体查找器entityResolver(XMLMapperEntityResolver)设置给了XPathParser的entityResolver属性,构造了XPathFactory,以及使用XPathFactory构造了节点解析的xpath属性,所以它起了一个名字叫commonConstructor--公共的构造器方法。我们看图:

②createDocument方法:
private Document createDocument(InputSource inputSource) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(this.validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(this.entityResolver);
builder.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception var4) {
throw new BuilderException("Error creating document instance. Cause: " + var4, var4);
}
}一看设置的这一堆估计一圈人都懵了,其实它就是指定工厂类按照何种规则去解析xml文档,没啥特殊的,大家可以自己写个示例用它去解析跟踪文档格式,这里就不细说。方法最后一句,builder.parse(inputSource),将我们的Reader包装成inputSource后,解析成文档树(文档工厂创建使用的设计模式就是典型的抽象工厂模式,大家可以学学)。我们看设置完属性的图:


我们可以看到,一行代码涉及到的后续过程挺多,但是,没什么可怕的。你深入研究后还觉得难吗?
这一行的作用就是完成了两个关键步骤:准备解析器,准备好解析的文档树。
其实,所有的配置文档解析都是一个套路,Spring中的源码解析也一样。
得到XPathParser后,我们XMLConfigBuilder构造函数的内容就全了,看代码:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}这此构造函数中, 首先调用了XMLConfigBuilder父类的构造函数,传入了Configuration对象,我们看看这个对象的构造函数:
public Configuration() {
this.safeRowBoundsEnabled = false;
this.safeResultHandlerEnabled = true;
this.mapUnderscoreToCamelCase = false;
this.aggressiveLazyLoading = true;
this.multipleResultSetsEnabled = true;
this.useGeneratedKeys = false;
this.useColumnLabel = true;
this.cacheEnabled = true;
this.callSettersOnNulls = false;
this.localCacheScope = LocalCacheScope.SESSION;
this.jdbcTypeForNull = JdbcType.OTHER;
this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
this.defaultExecutorType = ExecutorType.SIMPLE;
this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
this.variables = new Properties();
this.objectFactory = new DefaultObjectFactory();
this.objectWrapperFactory = new DefaultObjectWrapperFactory();
this.mapperRegistry = new MapperRegistry(this);
this.lazyLoadingEnabled = false;
this.interceptorChain = new InterceptorChain();
this.typeHandlerRegistry = new TypeHandlerRegistry();
this.typeAliasRegistry = new TypeAliasRegistry();
this.languageRegistry = new LanguageDriverRegistry();
this.mappedStatements = new Configuration.StrictMap("Mapped Statements collection");
this.caches = new Configuration.StrictMap("Caches collection");
this.resultMaps = new Configuration.StrictMap("Result Maps collection");
this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");
this.keyGenerators = new Configuration.StrictMap("Key Generators collection");
this.loadedResources = new HashSet();
this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");
this.incompleteStatements = new LinkedList();
this.incompleteCacheRefs = new LinkedList();
this.incompleteResultMaps = new LinkedList();
this.incompleteMethods = new LinkedList();
this.cacheRefMap = new HashMap();
this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
this.languageRegistry.register(RawLanguageDriver.class);
}不要被这么长一堆吓住,其实很简单,就是对Configuration类对象属性进行初始化, this.typeAliasRegistry.registerAlias("xxx", xxx.class);只是给对应的注册类起了个别名。上面的内容我们用到再说。
我们回到XMLConfigBuilder构造函数:
构造函数第一句,super(new Configuration());我们看下XMLConfigBuilder父类BaseBuilder的构造函数:
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}这里初始化了三个参数:configuration,typeAliasRegistry, typeHandlerRegistry。

其中typeAliasRegistry, typeHandlerRegistry就是使用了Configuration类构造函数初始化的对象:
this.typeHandlerRegistry = new TypeHandlerRegistry(); this.typeAliasRegistry = new TypeAliasRegistry();
一个是类型处理器注册类,一个是类型别名注册类,我们可以看下他们各自的构造函数。
TypeAliasRegistry别名注册类:它只是对我们常见的基本类型,数组等做了一个,保存在一个Map中(Map<String, Class<?>> TYPE_ALIASES)。(注册的含义不要想得太深奥,跟我们常见的网站注册一样,只是将你的基本信息录入,作为后续鉴别使用)
public TypeAliasRegistry() {
this.registerAlias("string", String.class);
this.registerAlias("byte", Byte.class);
this.registerAlias("long", Long.class);
this.registerAlias("short", Short.class);
this.registerAlias("int", Integer.class);
this.registerAlias("integer", Integer.class);
this.registerAlias("double", Double.class);
this.registerAlias("float", Float.class);
this.registerAlias("boolean", Boolean.class);
this.registerAlias("byte[]", Byte[].class);
this.registerAlias("long[]", Long[].class);
this.registerAlias("short[]", Short[].class);
this.registerAlias("int[]", Integer[].class);
this.registerAlias("integer[]", Integer[].class);
this.registerAlias("double[]", Double[].class);
this.registerAlias("float[]", Float[].class);
this.registerAlias("boolean[]", Boolean[].class);
this.registerAlias("_byte", Byte.TYPE);
this.registerAlias("_long", Long.TYPE);
this.registerAlias("_short", Short.TYPE);
this.registerAlias("_int", Integer.TYPE);
this.registerAlias("_integer", Integer.TYPE);
this.registerAlias("_double", Double.TYPE);
this.registerAlias("_float", Float.TYPE);
this.registerAlias("_boolean", Boolean.TYPE);
this.registerAlias("_byte[]", byte[].class);
this.registerAlias("_long[]", long[].class);
this.registerAlias("_short[]", short[].class);
this.registerAlias("_int[]", int[].class);
this.registerAlias("_integer[]", int[].class);
this.registerAlias("_double[]", double[].class);
this.registerAlias("_float[]", float[].class);
this.registerAlias("_boolean[]", boolean[].class);
this.registerAlias("date", Date.class);
this.registerAlias("decimal", BigDecimal.class);
this.registerAlias("bigdecimal", BigDecimal.class);
this.registerAlias("biginteger", BigInteger.class);
this.registerAlias("object", Object.class);
this.registerAlias("date[]", Date[].class);
this.registerAlias("decimal[]", BigDecimal[].class);
this.registerAlias("bigdecimal[]", BigDecimal[].class);
this.registerAlias("biginteger[]", BigInteger[].class);
this.registerAlias("object[]", Object[].class);
this.registerAlias("map", Map.class);
this.registerAlias("hashmap", HashMap.class);
this.registerAlias("list", List.class);
this.registerAlias("arraylist", ArrayList.class);
this.registerAlias("collection", Collection.class);
this.registerAlias("iterator", Iterator.class);
this.registerAlias("ResultSet", ResultSet.class);
}如图:

我们再看看TypeHandlerRegistry类型处理器注册类,它是将java类型和JdbcType都标记出对应的类型处理器,并且把对应的类型处理器都注册在各自的Map中:
public TypeHandlerRegistry() {
this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
this.register((JdbcType)JdbcType.TINYINT, (TypeHandler)(new ByteTypeHandler()));
this.register((Class)Short.class, (TypeHandler)(new ShortTypeHandler()));
this.register((Class)Short.TYPE, (TypeHandler)(new ShortTypeHandler()));
this.register((JdbcType)JdbcType.SMALLINT, (TypeHandler)(new ShortTypeHandler()));
this.register((Class)Integer.class, (TypeHandler)(new IntegerTypeHandler()));
this.register((Class)Integer.TYPE, (TypeHandler)(new IntegerTypeHandler()));
this.register((JdbcType)JdbcType.INTEGER, (TypeHandler)(new IntegerTypeHandler()));
this.register((Class)Long.class, (TypeHandler)(new LongTypeHandler()));
this.register((Class)Long.TYPE, (TypeHandler)(new LongTypeHandler()));
this.register((Class)Float.class, (TypeHandler)(new FloatTypeHandler()));
this.register((Class)Float.TYPE, (TypeHandler)(new FloatTypeHandler()));
this.register((JdbcType)JdbcType.FLOAT, (TypeHandler)(new FloatTypeHandler()));
this.register((Class)Double.class, (TypeHandler)(new DoubleTypeHandler()));
this.register((Class)Double.TYPE, (TypeHandler)(new DoubleTypeHandler()));
this.register((JdbcType)JdbcType.DOUBLE, (TypeHandler)(new DoubleTypeHandler()));
this.register((Class)String.class, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
this.register((Class)String.class, JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
this.register((Class)String.class, JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((Class)String.class, JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((Class)String.class, JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
this.register((JdbcType)JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
this.register((JdbcType)JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
this.register((JdbcType)JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
this.register((JdbcType)JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
this.register((JdbcType)JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((JdbcType)JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((JdbcType)JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
this.register((Class)Object.class, JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
this.register((JdbcType)JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
this.register((Class)BigInteger.class, (TypeHandler)(new BigIntegerTypeHandler()));
this.register((JdbcType)JdbcType.BIGINT, (TypeHandler)(new LongTypeHandler()));
this.register((Class)BigDecimal.class, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.REAL, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.DECIMAL, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.NUMERIC, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((Class)Byte[].class, (TypeHandler)(new ByteObjectArrayTypeHandler()));
this.register((Class)Byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
this.register((Class)Byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
this.register((Class)byte[].class, (TypeHandler)(new ByteArrayTypeHandler()));
this.register((Class)byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
this.register((Class)byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
this.register((JdbcType)JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
this.register((JdbcType)JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
this.register(Object.class, this.UNKNOWN_TYPE_HANDLER);
this.register(Object.class, JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
this.register(JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
this.register((Class)Date.class, (TypeHandler)(new DateTypeHandler()));
this.register((Class)Date.class, JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
this.register((Class)Date.class, JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
this.register((JdbcType)JdbcType.TIMESTAMP, (TypeHandler)(new DateTypeHandler()));
this.register((JdbcType)JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
this.register((JdbcType)JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
this.register((Class)java.sql.Date.class, (TypeHandler)(new SqlDateTypeHandler()));
this.register((Class)Time.class, (TypeHandler)(new SqlTimeTypeHandler()));
this.register((Class)Timestamp.class, (TypeHandler)(new SqlTimestampTypeHandler()));
this.register((Class)Character.class, (TypeHandler)(new CharacterTypeHandler()));
this.register((Class)Character.TYPE, (TypeHandler)(new CharacterTypeHandler()));
} 如图:

最终设置完的XMLConfigBuilder属性如图,其中最关键的就是configuration,这是所有解析配置的核心的核心。
这里props我们没有传值为null,到了解析properties标签属性时,大家就可以看到这个值做什么用了。

三、【核心方法】this.build(parser.parse())解密
我们先看parser.parse()。parser指的是我们第一步得到的XMLConfigBuilder,在它的parse方法中:
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}里面的关键方法this.parseConfiguration(this.parser.evalNode("/configuration")):
this.parser.evalNode("/configuration")标记出根节点,然后从根节点开始解析各属性。
这里就是MyBatis的核心配置文件中可以配置的所有节点啦!!
比如,我们配置的properties节点,就是使用 this.propertiesElement(root.evalNode("properties"));来解析,这里面就要用到我们核心的核心---configuration对象,一个对象包含了很多信息。这一个解析方法包含了我们db-core.xml文件的所有解析内容。
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.settingsElement(root.evalNode("settings"));
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}这里的每一个解析方法都可以作为作为一节来讲,我们后续帖逐渐来阐述。
解析完核心配置文件的所有标签后,我们的configuration对象信息就都完善了,此时SqlSessionFactoryBuilder的build开始执行,去得到SqlSessionFactory,这里使用了DefaultSqlSessionFactory。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}在这里,只是把Configuration对象传到了DefaultSqlSessionFactory的构造方法中,这样SqlSessionFactory接口终于有了自己的实现类DefaultSqlSessionFactory。
以上就是SqlSessionFactory获取的所有步骤,虽然过程繁杂,其实很简单。
四、properties标签解析和elements标签解析
先来说两个我们用到的,properties标签解析和elements标签解析,解答我们开头说的问题。
1、properties标签的解析
properties标签的解析使用的方法是this.propertiesElement(root.evalNode("properties"))。

首先使用root.evalNode("properties")得到我们properties标签的内容,因此context值为:
<properties resource="db-info.properties"/>
其中,Properties defaults = context.getChildrenAsProperties()是用来获取properties标签下的子元素内容的,比如:
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>
通过这一行代码,就可以获取到property元素内容,存放在Properties(它是一个hashTable)中。
这里我们没有子元素,因此,defaults中没有内容。
我们继续往下,在properties标签中,我们使用了resource属性,未使用url属性。
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");因此,上面得到的resource值为db-info.properties,url为null。下面的判断也告诉我们,在properties标签中,resource和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.");
}这里我们resource不为空,使用Resources.getResourceAsProperties(resource)获取到属性的key-value值,全部放在defaults中。在db-info.properties中,我们放了数据源的四个属性,因此,这里最后的大小为4。
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
下面,从configuration对象中,获取到Variables内容,这个内容是啥?