xml文件解析的本质
mybatis中有两类xml文件需要解析,一类是mybatis配置文件,另一类是mybatis的mapper文件。
解析的本质就是将配置文件中的数据信息,解析存储到java对象当中,然后加载到内存使用。
mybatis配置文件的解析
XMLConfigBuilder.java 是用来解析mybatis配置文件的。
解析配置文件是由 SqlSessionFactoryBuilder创建时,调用build构建SqlSessionFactoryBuilder对象时,调用XMLConfigBuilder实现解析。
XMLConfigBuilder中解析配置文件的主要方法
java中方法解析配置文件
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
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);
}
}
mybatis配置文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1、属性:例如jdbc.properties -->
<properties resource="jdbc.properties"></properties>
<!-- 2、设置:定义全局性设置,例如开启二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 3、类型名称:为一些类定义别名 -->
<typeAliases>
<typeAlias type="com.panshenlian.pojo.User" alias="user"></typeAlias>
</typeAliases>
<!-- 4、类型处理器:定义Java类型与数据库中的数据类型之间的转换关系 -->
<typeHandlers></typeHandlers>
<!-- 5、对象工厂 -->
<objectFactory type=""></objectFactory>
<!-- 6、插件:mybatis的插件,支持自定义插件 -->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<!-- 7、环境:配置mybatis的环境 -->
<environments default="development">
<!-- 环境变量:支持多套环境变量,例如开发环境、生产环境 -->
<environment id="development">
<!-- 事务管理器:默认JDBC -->
<transactionManager type="JDBC" />
<!-- 数据源:使用连接池,并加载mysql驱动连接数据库 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 8、数据库厂商标识 -->
<databaseIdProvider type=""></databaseIdProvider>
<!-- 9、映射器:指定映射文件或者映射类 -->
<mappers>
<mapper resource="UserMapper.xml" />
</mappers>
</configuration>
注意:Mybatis配置文件的属性位置顺序是 固定 的,不允许 颠倒顺序,否则 Mybatis 解析 XML 文件的时候就会抛出异常。
mybatis Mapper文件的解析
XMLConfigBuilder解析mybatis配置文件,最后一个标签就是 mappers 。
//mybatis配置文件的解析
mapperElement(root.evalNode("mappers"));
代码中的标签就是对应于 映射器(mappers) 中的标签。
java中mappers标签
映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<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>
注意 :
扫描接口或者扫描xml文件。如果是扫描的是接口,那么xml和接口要在同一目录下或者指定xml文件所在目录。如果扫描的是xml文件,就不需要扫描接口。
扫描接口为啥需要再次扫描xml文件:使用mapper接口,无法得知xml文件在哪。
扫描xml文件为啥不需要扫描接口:xml文件中的 namespace就是mapper接口 全限定名。
XMLMapperBuilder 解析mapper xml文件的核心
解析mapper 代码
public void parse() {
// 判断资源是否重复加载 //进入判断 是 首次加载
if (!configuration.isResourceLoaded(resource)) {
//解析 mapper xml 文件
configurationElement(parser.evalNode("/mapper"));
//添加资源到配置对象中 用于检测是否 首次加载
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
解析mapper 文件内容的代码
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// java 对象属性 与 数据库字段 对照关系
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
// xml 文件编写的SQL语句
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
小结:
SqlSessionFactoryBuilder创建时,创建了XMLConfigBuilder对象,同时XMLConfigBuilder又创建了XMLMapperBuilder对象。调用两个builder对象完成mybatis中xml的解析,至于细节处调用的对象就不复述了。
问题来了,创建的对象最后去哪里了那?
创建SqlSessionFactoryBuilder 使用 build()方法,返回的是SqlSessionFactory对象,但是生成SqlSessionFactory对象时需要,一个入参Configuration对象。
根据上面的代码往下跟踪,得到 XMLConfigBuilder 对象 最后 使用 super(new Configuration()) 生成的Configuration对象。
parseConfiguration方法中,所有解析后的xml数据都是存放在Configuration对象当中。
小结:
Configuration对象存储了,所有的xml解析后的数据。