Mybatis如何加载配置文件 源码解读parameterType

柔光的暖阳◎ 2022-09-30 11:52 207阅读 0赞

我能学到什么

-————————————————————————————————————————————————————————————————————————-

  1. Mybatis加载解析配置文件流程

  2. 如何解析配置文件里面的parameterType

  3. 提高看源码的能力

  4. 查看源码编写方式,明白应该如何规范的写出解析XML文件的代码,提高编码能力

  5. 学会使用框架上解决问题的思维和常用手段

    • l 利用配置文件解耦
    • l 利用反射、动态代理、泛型、设计模式等解决框架级别的问题。
    • l 框架的设计思想

-—————————————————————————————————————————————————————————————————————————-

题外问题

如下一段代码:调用了Mybatis提供的加载及解析配置文件功能。

  1. public class DBAccess {
  2. public SqlSession getSqlSession() throws IOException
  3. {
  4. //1、通过配置文件获取数据库连接相关信息
  5. Readerreader=Resources.getResourceAsReader("hdu/terence/config/Configuration.xml");
  6. //2、通过配置信息构建SqlSessionFactory
  7. SqlSessionFactorySSF=new SqlSessionFactoryBuilder().build(reader);
  8. //3、通过SqlSessionFactory打开数据库会话
  9. SqlSessionsqlSession=SSF.openSession();
  10. return sqlSession;
  11. }
  12. }

题外话

上述代码在Mybatis中的使用存在两个问题:

问题一:每次访问数据调用Sql语句的时候,都会临时的去调用加载配置文件解析,很耗性能。

问题二:另外,每次访问,都需要反复加载,耗费时间。

这个问题的暂且说一下解决办法:

针对第一个配置文件加载的时机问题,要自己写一个监听器,容器在启动的时候加载配置文件。

【加载时机】—>【监听器】

针对第二个问题,通过单利模式存放监听器加载的配置内容,防止其重复加载。

【重复加载】—>【单例模式】

在实际开发中则是通过Spring+Mybatis解决上述两个问题的。

源码解读

加载上篇

先说一下上述加载解析配置文件的代码:根据注释可知,此部分分为三步。

  1. 通过配置文件获取数据库连接相关信息

  2. 通过配置信息构建SqlSessionFactory

  3. 通过SqlSessionFactory打开数据库会话

其中,在第二步通过build()方法进入Mybatis当中。

上述build()方法是在SqlSessionFactoryBuilder.class这个类中实现的:

  1. public SqlSessionFactorybuild(Reader reader, String environment, Properties properties) {
  2. try {
  3. XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
  4. return build(parser.parse());
  5. } catch (Exception e) {
  6. throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  7. } finally {
  8. ErrorContext.instance().reset();
  9. try {
  10. reader.close();
  11. } catch (IOException e) {
  12. // Intentionally ignore. Prefer previouserror.
  13. }
  14. }
  15. }

看此句:

XMLConfigBuilderparser = new XMLConfigBuilder(reader, environment, properties)表示将转换后的reader、配置环境以及配置的各个属性包装在parser解析项中,然后通过return build(parser.parse())返回一个会话工厂,仍然需要进入另外的源码XMLConfigBilder.class中,找到parser()解析方法:

  1. private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  2. super(new Configuration());
  3. ErrorContext.instance().resource("SQL Mapper Configuration");
  4. this.configuration.setVariables(props);
  5. this.parsed = false;
  6. this.environment = environment;
  7. this.parser = parser;
  8. }
  9. public Configuration parse() {
  10. if (parsed) {
  11. throw new BuilderException("Each XMLConfigBuilder can only beused once.");
  12. }
  13. parsed = true;
  14. parseConfiguration(parser.evalNode("/configuration"));
  15. return configuration;
  16. }

在构造函数当中,将parsed=flase,在configuration parse()方法中,先判别parsed是否为ture,如果是True,则表示已经加载解析过,抛出异常(防止重复加载,耗费性能耗费时间,防止出现类似于调用sql语句时候每次都调用DBAccesss一样出现的两个问题),否则将其赋值为true,然后继续解析加载文件,通过parser.evalNode(“/configuration”)进入XPathParser.Class中,通过里面的Document文件读取对象来解析文件(那么由此可以说明,Mybatis解析xml文件使用的是Dom对象和Java JDK中的类进行的)。

XPathParser.Class文件相关内容:

构造函数:

  1. public XPathParser(String xml) {
  2. commonConstructor(false, null, null);
  3. this.document = createDocument(new InputSource(new StringReader(xml)));
  4. }
  5. public XPathParser(Reader reader) {
  6. commonConstructor(false, null, null);
  7. this.document = createDocument(new InputSource(reader));
  8. }

看第二个构造函数可知此步表示reader对象的转化为输入流,然后转化为document对象。

配置文件

上述的源码目的是为了进入配置文件Configuration.xml文件解析,下面先贴出来配置文件

贴总配置文件Configuration.xml:

  1. <?xml version="1.0"encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <!-- 配置声明拦截器,可在拦截器中获取该配置中的属性值,用作其他用途 -->
  7. <plugins>
  8. <plugin interceptor="hdu.terence.interceptor.PageInterceptor">
  9. <property name="test"value="123"/>
  10. </plugin>
  11. </plugins>
  12. <environments default="development">
  13. <environment id="development">
  14. <transactionManager type="JDBC">
  15. <property name="" value=""/>
  16. </transactionManager>
  17. <dataSource type="UNPOOLED">
  18. <property name="driver" value="com.mysql.jdbc.Driver"/>
  19. <!--url连接,注意编码方式的指定-->
  20. <property name="url" value="jdbc:mysql://127.0.0.1:3306/micromessage?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/>
  21. <property name="username" value="root"/>
  22. <property name="password" value="root"/>
  23. </dataSource>
  24. </environment>
  25. </environments>
  26. <!--映射配置文件,多个配置文件可以写多个mapper映射-->
  27. <mappers>
  28. <mapper resource="hdu/terence/config/sqlxml/Message.xml"/>
  29. <mapper resource="hdu/terence/config/sqlxml/Command.xml"/>
  30. <mapper resource="hdu/terence/config/sqlxml/CommandContent.xml"/>
  31. </mappers>
  32. </configuration>

子配置文件Dao.xml—— Message.xml

  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="hdu.terence.dao.IMessage">
  6. <resultMap type="hdu.terence.bean.Message" id="MessageResult">
  7. <!--存放Dao值--> <!--type是和数据库对应的bean类名Message-->
  8. <id column="id" jdbcType="INTEGER"property="id"/> <!--主键标签-->
  9. <result column="COMMAND" jdbcType="VARCHAR"property="command"/>
  10. <result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
  11. <result column="CONTENT" jdbcType="VARCHAR"property="content"/>
  12. </resultMap>
  13. <select id="queryMessageList" parameterType="java.util.Map" resultMap="MessageResult">
  14. select <include refid="columns"/> from MESSAGE
  15. <where>
  16. <if test="message.command != null and!"".equals(message.command.trim())">
  17. andCOMMAND=#{message.command}
  18. </if>
  19. <if test="message.description != null and!"".equals(message.description.trim())">
  20. andDESCRIPTION like '%' #{message.description} '%'
  21. </if>
  22. </where>
  23. order by ID limit#{page.dbIndex},#{page.dbNumber}
  24. </select>
  25. </mapper>

梳理总流程

OK,退出来梳理总流程:

首先,建立一个总配置文件的解析流,使用Document对象代替reader对象,成为了新的配置文件解析代言人,然后Document对象进入Configuration.xml配置文件,解析出……的配置项,找到Dao.xml配置文件的路径,根据该路径,进入该配置文件(Message.xml)。

最后,进入Dao.xml配置文件之后,使用JDK中的Dom对象逐个解析,肯定能找到节点—>获取该节点的parameterType属性—>根据该属性值利用反射获取对应类名。

OK,完毕!

发表评论

表情:
评论列表 (有 0 条评论,207人围观)

还没有评论,来说两句吧...

相关阅读