MyBatis源码阅读——MyBatis初始化流程解析

╰半夏微凉° 2022-05-23 11:11 377阅读 0赞

前言

在之前的几篇文章中,我们在源码中看到到了很多类,比如mapperRegistry、mappedStatements等,虽然我们知道它们都是在MyBatis初始化的时候完成加载的,那么我们还是有必要去了解一下其加载过程。
还是跟之前一样,写一个demo,去边debug 边阅读源码。

  1. public static void main(String[] args) throws IOException {
  2. String resource = "mybatis/conf/mybatis-config.xml";
  3. InputStream inputStream = Resources.getResourceAsStream(resource);
  4. //从 XML 中构建 SqlSessionFactory
  5. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  6. SqlSession session = sqlSessionFactory.openSession();
  7. try {
  8. BlogMapper mapper = session.getMapper(BlogMapper.class);
  9. Blog blog = mapper.selectBlog(1L);
  10. System.out.println(blog);
  11. blog = mapper.selectBlog(1L);
  12. System.out.println(blog);
  13. } finally {
  14. session.close();
  15. }
  16. }

SqlSessionFactory的产生

我们直接debug进入org.apache.ibatis.session.SqlSessionFactoryBuilder

  1. public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  2. try {
  3. //获取配置信息 mybatis-config.xml
  4. XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
  5. //解析配置并初始化建立SqlSessionFactory
  6. return build(parser.parse());
  7. } catch (Exception e) {
  8. throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  9. } finally {
  10. ErrorContext.instance().reset();
  11. try {
  12. inputStream.close();
  13. } catch (IOException e) {
  14. // Intentionally ignore. Prefer previous error.
  15. }
  16. }
  17. }

进入parser.parse() -> parseConfiguration(parser.evalNode(“/configuration”)); 这里是分析配置并加载初始化类的入口。

  1. private void parseConfiguration(XNode root) {
  2. try {
  3. //先加载properties。这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。
  4. propertiesElement(root.evalNode("properties"));
  5. //加载别名。类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余
  6. typeAliasesElement(root.evalNode("typeAliases"));
  7. //加载插件plugin
  8. pluginElement(root.evalNode("plugins"));
  9. //MyBatis 每次在创建结果对象的新实例时, 是使用 ObjectFactory (对象工厂)实例来完成的。如果有自定义,则读取配置中的自定义的类
  10. objectFactoryElement(root.evalNode("objectFactory"));
  11. //结果对象转换(驼峰法等)
  12. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  13. //ReflectorFactory
  14. reflectionFactoryElement(root.evalNode("reflectionFactory"));
  15. //一些settings
  16. settingsElement(root.evalNode("settings"));
  17. // read it after objectFactory and objectWrapperFactory issue #631
  18. //数据库连接、事务管理器的配置
  19. environmentsElement(root.evalNode("environments"));
  20. //databaseIdProvider的配置,可以根据不用的id产生不同的sql语句
  21. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  22. //加载负责java数据类型和jdbc数据类型之间的映射和转换 javaType\jdbcType 这些
  23. typeHandlerElement(root.evalNode("typeHandlers"));
  24. //加载映射文件Mapper(划重点)
  25. mapperElement(root.evalNode("mappers"));
  26. } catch (Exception e) {
  27. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  28. }
  29. }

加载映射文件Mapper的过程解析->mapperElement()

  1. private void mapperElement(XNode parent) throws Exception {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) {
  5. String mapperPackage = child.getStringAttribute("name");
  6. configuration.addMappers(mapperPackage);
  7. } else {
  8. String resource = child.getStringAttribute("resource");
  9. String url = child.getStringAttribute("url");
  10. String mapperClass = child.getStringAttribute("class");
  11. if (resource != null && url == null && mapperClass == null) {
  12. ErrorContext.instance().resource(resource);
  13. InputStream inputStream = Resources.getResourceAsStream(resource);
  14. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
  15. mapperParser.parse();
  16. } else if (resource == null && url != null && mapperClass == null) {
  17. ErrorContext.instance().resource(url);
  18. InputStream inputStream = Resources.getUrlAsStream(url);
  19. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  20. mapperParser.parse();
  21. } else if (resource == null && url == null && mapperClass != null) {
  22. Class<?> mapperInterface = Resources.classForName(mapperClass);
  23. configuration.addMapper(mapperInterface);
  24. } else {
  25. throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  26. }
  27. }
  28. }
  29. }
  30. }

这段代码的核心,Mapper的注册等操作都在此

  1. XMLMapperBuilder mapperParser = new XMLMapperBuilder();
  2. mapperParser.parse();

二级缓存相关

XMLMapperBuilder中关联了 MapperBuilderAssistant 对象,而MapperBuilderAssistant 中关联了Cache对象,Cache是二级缓存的核心,而它在映射文件Mapper初始化的时候附带,那说明它的生命周期是基于Mapper的。

发表评论

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

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

相关阅读