我能学到什么
-————————————————————————————————————————————————————————————————————————-
Mybatis加载解析配置文件流程
如何解析配置文件里面的parameterType
提高看源码的能力
查看源码编写方式,明白应该如何规范的写出解析XML文件的代码,提高编码能力
学会使用框架上解决问题的思维和常用手段
- l 利用配置文件解耦
- l 利用反射、动态代理、泛型、设计模式等解决框架级别的问题。
- l 框架的设计思想
-—————————————————————————————————————————————————————————————————————————-
题外问题
如下一段代码:调用了Mybatis提供的加载及解析配置文件功能。
public class DBAccess {
public SqlSession getSqlSession() throws IOException
{
//1、通过配置文件获取数据库连接相关信息
Readerreader=Resources.getResourceAsReader("hdu/terence/config/Configuration.xml");
//2、通过配置信息构建SqlSessionFactory
SqlSessionFactorySSF=new SqlSessionFactoryBuilder().build(reader);
//3、通过SqlSessionFactory打开数据库会话
SqlSessionsqlSession=SSF.openSession();
return sqlSession;
}
}
题外话
上述代码在Mybatis中的使用存在两个问题:
问题一:每次访问数据调用Sql语句的时候,都会临时的去调用加载配置文件解析,很耗性能。
问题二:另外,每次访问,都需要反复加载,耗费时间。
这个问题的暂且说一下解决办法:
针对第一个配置文件加载的时机问题,要自己写一个监听器,容器在启动的时候加载配置文件。
【加载时机】—>【监听器】
针对第二个问题,通过单利模式存放监听器加载的配置内容,防止其重复加载。
【重复加载】—>【单例模式】
在实际开发中则是通过Spring+Mybatis解决上述两个问题的。
源码解读
加载上篇
先说一下上述加载解析配置文件的代码:根据注释可知,此部分分为三步。
通过配置文件获取数据库连接相关信息
通过配置信息构建SqlSessionFactory
通过SqlSessionFactory打开数据库会话
其中,在第二步通过build()方法进入Mybatis当中。
上述build()方法是在SqlSessionFactoryBuilder.class这个类中实现的:
public SqlSessionFactorybuild(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 previouserror.
}
}
}
看此句:
XMLConfigBuilderparser = new XMLConfigBuilder(reader, environment, properties)表示将转换后的reader、配置环境以及配置的各个属性包装在parser解析项中,然后通过return build(parser.parse())返回一个会话工厂,仍然需要进入另外的源码XMLConfigBilder.class中,找到parser()解析方法:
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;
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only beused once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在构造函数当中,将parsed=flase,在configuration parse()方法中,先判别parsed是否为ture,如果是True,则表示已经加载解析过,抛出异常(防止重复加载,耗费性能耗费时间,防止出现类似于调用sql语句时候每次都调用DBAccesss一样出现的两个问题),否则将其赋值为true,然后继续解析加载文件,通过parser.evalNode(“/configuration”)进入XPathParser.Class中,通过里面的Document文件读取对象来解析文件(那么由此可以说明,Mybatis解析xml文件使用的是Dom对象和Java JDK中的类进行的)。
XPathParser.Class文件相关内容:
构造函数:
public XPathParser(String xml) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(reader));
}
看第二个构造函数可知此步表示reader对象的转化为输入流,然后转化为document对象。
配置文件
上述的源码目的是为了进入配置文件Configuration.xml文件解析,下面先贴出来配置文件
贴总配置文件Configuration.xml:
<?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>
<!-- 配置声明拦截器,可在拦截器中获取该配置中的属性值,用作其他用途 -->
<plugins>
<plugin interceptor="hdu.terence.interceptor.PageInterceptor">
<property name="test"value="123"/>
</plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--url连接,注意编码方式的指定-->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/micromessage?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--映射配置文件,多个配置文件可以写多个mapper映射-->
<mappers>
<mapper resource="hdu/terence/config/sqlxml/Message.xml"/>
<mapper resource="hdu/terence/config/sqlxml/Command.xml"/>
<mapper resource="hdu/terence/config/sqlxml/CommandContent.xml"/>
</mappers>
</configuration>
子配置文件Dao.xml—— Message.xml
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="hdu.terence.dao.IMessage">
<resultMap type="hdu.terence.bean.Message" id="MessageResult">
<!--存放Dao值--> <!--type是和数据库对应的bean类名Message-->
<id column="id" jdbcType="INTEGER"property="id"/> <!--主键标签-->
<result column="COMMAND" jdbcType="VARCHAR"property="command"/>
<result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
<result column="CONTENT" jdbcType="VARCHAR"property="content"/>
</resultMap>
<select id="queryMessageList" parameterType="java.util.Map" resultMap="MessageResult">
select <include refid="columns"/> from MESSAGE
<where>
<if test="message.command != null and!"".equals(message.command.trim())">
andCOMMAND=#{message.command}
</if>
<if test="message.description != null and!"".equals(message.description.trim())">
andDESCRIPTION like '%' #{message.description} '%'
</if>
</where>
order by ID limit#{page.dbIndex},#{page.dbNumber}
</select>
</mapper>
梳理总流程
OK,退出来梳理总流程:
首先,建立一个总配置文件的解析流,使用Document对象代替reader对象,成为了新的配置文件解析代言人,然后Document对象进入Configuration.xml配置文件,解析出……的配置项,找到Dao.xml配置文件的路径,根据该路径,进入该配置文件(Message.xml)。
最后,进入Dao.xml配置文件之后,使用JDK中的Dom对象逐个解析,肯定能找到节点—>获取该节点的parameterType属性—>根据该属性值利用反射获取对应类名。
OK,完毕!
还没有评论,来说两句吧...