Mybatis是如何解析配置文件的?看完终于明白了

开发 前端
在以前文章中,我们把Mybatis源码阅读的整个流程梳理了一遍。今天,我们来详细聊聊,Mybatis是如何解析配置文件的。

[[357645]]

在以前文章中,我们把Mybatis源码阅读的整个流程梳理了一遍。今天,我们来详细聊聊,Mybatis是如何解析配置文件的。

这是今天分析的流程图:

 

还是从案例开始。

demo案例

  1. public static void main(String[] args) { 
  2.         String resource = "mybatis-config.xml"
  3.         InputStream inputStream = null
  4.         SqlSession sqlSession = null
  5.         try { 
  6.             inputStream = Resources.getResourceAsStream(resource); 
  7.             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 
  8.             sqlSession = sqlSessionFactory.openSession(); 
  9.  
  10.             UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 
  11.             System.out.println(userMapper.selectById(1)); 
  12.  
  13.         } catch (Exception e) { 
  14.             e.printStackTrace(); 
  15.         } finally { 
  16.             try { 
  17.                 inputStream.close(); 
  18.             } catch (IOException e) { 
  19.                 e.printStackTrace(); 
  20.             } 
  21.             sqlSession.close(); 
  22.         } 
  23.     } 

见证奇迹

从SqlSessionFactoryBuilder开始。

SqlSessionFactoryBuilder类

  1. org.apache.ibatis.session.SqlSessionFactoryBuilder 

该类里全是build方法各种重载。

  1. //这个方法啥也没干   
  2. public SqlSessionFactory build(InputStream inputStream) { 
  3.     return build(inputStream, nullnull); 
  4.  } 

最终来到另外一个build方法里:

  1. public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { 
  2.    try { 
  3.      //创建一个XMLConfigBuilder对象   
  4.      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 
  5.      return build(parser.parse()); 
  6.    } catch (Exception e) { 
  7.      throw ExceptionFactory.wrapException("Error building SqlSession.", e); 
  8.    } finally { 
  9.      ErrorContext.instance().reset(); 
  10.      try { 
  11.        inputStream.close(); 
  12.      } catch (IOException e) { 
  13.        // Intentionally ignore. Prefer previous error. 
  14.      } 
  15.    } 
  16.  } 

XMLConfigBuilder类

该类的构造方法重载:

 

首先进入:

  1. public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { 
  2.     this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment,      
  3.          props); 
  4. private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { 
  5.     super(new Configuration()); 
  6.     ErrorContext.instance().resource("SQL Mapper Configuration"); 
  7.     this.configuration.setVariables(props); 
  8.     this.parsed = false
  9.     this.environment = environment; 
  10.     this.parser = parser; 

build(parser.parse());中的parser.parse();

mybatis-config.xml在哪里解析的呢?

请看下面这个方法:

  1. //该方法返回一个Configuration对象 
  2. public Configuration parse() { 
  3.   if (parsed) { 
  4.     throw new BuilderException("Each XMLConfigBuilder can only be used once."); 
  5.   } 
  6.   parsed = true
  7.   //关键点 
  8.   parseConfiguration(parser.evalNode("/configuration")); 
  9.   return configuration; 

parseConfiguration(parser.evalNode("/configuration"));

终于看到开始解析配置文件了:

 

进入方法parseConfiguration。

  1. private void parseConfiguration(XNode root) { 
  2.   try { 
  3.     //issue #117 read properties first 
  4.     propertiesElement(root.evalNode("properties")); 
  5.     Properties settings = settingsAsProperties(root.evalNode("settings")); 
  6.     loadCustomVfs(settings); 
  7.     loadCustomLogImpl(settings); 
  8.     typeAliasesElement(root.evalNode("typeAliases")); 
  9.     pluginElement(root.evalNode("plugins")); 
  10.     objectFactoryElement(root.evalNode("objectFactory")); 
  11.     objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 
  12.     reflectorFactoryElement(root.evalNode("reflectorFactory")); 
  13.     settingsElement(settings); 
  14.     // read it after objectFactory and objectWrapperFactory issue #631 
  15.     environmentsElement(root.evalNode("environments")); 
  16.     databaseIdProviderElement(root.evalNode("databaseIdProvider")); 
  17.     typeHandlerElement(root.evalNode("typeHandlers")); 
  18.     mapperElement(root.evalNode("mappers")); 
  19.   } catch (Exception e) { 
  20.     throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 
  21.   } 

这里就是把mybatis-config.xml内容解析,然后设置到Configuration对象中。

那么我们定义的Mapper.xml是在哪里解析的呢?

我们的Mapper.xml在mybatis-config.xml中的配置是这样的:

 

使用方式有以下四种:

  1. //1使用类路径 
  2. <mappers> 
  3.     <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> 
  4.       <mapper resource="org/mybatis/builder/BlogMapper.xml"/> 
  5.    <mapper resource="org/mybatis/builder/PostMapper.xml"/> 
  6. </mappers> 
  7. //2使用绝对url路径 
  8. <mappers> 
  9.    <mapper url="file:///var/mappers/AuthorMapper.xml"/> 
  10.    <mapper url="file:///var/mappers/BlogMapper.xml"/> 
  11.    <mapper url="file:///var/mappers/PostMapper.xml"/> 
  12. </mappers> 
  13. //3使用java类名 
  14. <mappers> 
  15.    <mapper class="org.mybatis.builder.AuthorMapper"/> 
  16.    <mapper class="org.mybatis.builder.BlogMapper"/> 
  17.    <mapper class="org.mybatis.builder.PostMapper"/> 
  18. </mappers> 
  19.  
  20. //4自动扫描包下所有映射器 
  21. <mappers> 
  22.    <package name="org.mybatis.builder"/> 
  23. </mappers> 

继续源码分析,我们在上面mybatis-config.xml解析中可以看到:

 

我们不妨进入这个方法看看:

  1. private void mapperElement(XNode parent) throws Exception { 
  2.    if (parent != null) { 
  3.      for (XNode child : parent.getChildren()) { 
  4.        //自动扫描包下所有映射器 
  5.        if ("package".equals(child.getName())) { 
  6.          String mapperPackage = child.getStringAttribute("name"); 
  7.          //放   
  8.          configuration.addMappers(mapperPackage); 
  9.        } else { 
  10.          String resource = child.getStringAttribute("resource"); 
  11.          String url = child.getStringAttribute("url"); 
  12.          String mapperClass = child.getStringAttribute("class"); 
  13.          //使用java类名 
  14.          if (resource != null && url == null && mapperClass == null) { 
  15.            ErrorContext.instance().resource(resource); 
  16.             //根据文件存放目录,读取XxxMapper.xml 
  17.            InputStream inputStream = Resources.getResourceAsStream(resource); 
  18.             //映射器比较复杂,调用XMLMapperBuilder 
  19.            //注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析 
  20.            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); 
  21.            mapperParser.parse(); 
  22.          //使用绝对url路径 
  23.          } else if (resource == null && url != null && mapperClass == null) { 
  24.            ErrorContext.instance().resource(url); 
  25.            InputStream inputStream = Resources.getUrlAsStream(url); 
  26.            //映射器比较复杂,调用XMLMapperBuilder 
  27.            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); 
  28.            mapperParser.parse(); 
  29.          //使用类路径     
  30.          } else if (resource == null && url == null && mapperClass != null) { 
  31.            Class<?> mapperInterface = Resources.classForName(mapperClass); 
  32.            //直接把这个映射加入配置 
  33.            configuration.addMapper(mapperInterface); 
  34.          } else { 
  35.            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); 
  36.          } 
  37.        } 
  38.      } 
  39.    } 
  40.  } 

这里刚刚和我们的上面说的使用的方式完全是一模一样的。

到这里,配置文件mybatis-config.xml和我们定义映射文件XxxMapper.xml就全部解析完成。

回到SqlSessionFactoryBuilder类

前面讲到了XMLConfigBuilder中的parse方法,并返回了一个Configuration对象。

build(parser.parse());

这个build方法就是传入一个Configuration对象,然后构建一个DefaultSqlSession对象。

  1. public SqlSessionFactory build(Configuration config) { 
  2.   return new DefaultSqlSessionFactory(config); 

继续回到我们的demo代码中这一行代码里:

  1. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 

这一行代码就相当于:

  1. SqlSessionFactory sqlSessionFactory = new new DefaultSqlSessionFactory(); 

 

到这里,我们的整个流程就搞定了。

本文转载自微信公众号「 Java后端技术全栈  」,可以通过以下二维码关注。转载本文请联系 Java后端技术全栈  公众号。

 

责任编辑:武晓燕 来源: Java后端技术全栈
相关推荐

2021-07-08 21:19:04

BashLinux

2021-11-09 06:49:20

WiFi 6WiFi 5通信网络

2021-06-28 21:04:09

显示器花屏电脑

2020-01-06 08:40:11

阿里场景服务

2021-07-26 05:00:16

算法DfsBfs

2022-02-25 07:07:04

扩展坞电脑笔记本

2010-02-03 09:19:31

Python模块

2022-12-31 08:17:02

2011-03-28 09:07:26

Nagios配置文件

2021-06-04 07:13:43

Logger TRACEDEBUG

2022-08-08 20:23:14

一网统管企业协商

2021-06-13 12:03:46

SaaS软件即服务

2021-10-09 00:02:04

DevOps敏捷开发

2022-03-27 20:32:28

Knative容器事件模型

2024-01-17 07:12:26

MySQL配置文件数据库

2018-03-09 10:02:23

iPhone X下巴苹果

2021-09-16 12:10:24

物联网互联网应用

2021-09-26 15:58:05

MySQL SQL 语句数据库

2022-05-01 22:09:27

数据模型大数据

2020-10-21 14:33:01

接口主流显示器
点赞
收藏

51CTO技术栈公众号