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

前言

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

 public static void main(String[] args) throws IOException {
        String resource = "mybatis/conf/mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //从 XML 中构建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            Blog blog = mapper.selectBlog(1L);
            System.out.println(blog);
            blog = mapper.selectBlog(1L);
            System.out.println(blog);
        } finally {
            session.close();
        }
    }

SqlSessionFactory的产生

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

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
       //获取配置信息 mybatis-config.xml
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //解析配置并初始化建立SqlSessionFactory 
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

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

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

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

 private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

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

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

二级缓存相关

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

猜你喜欢

转载自blog.csdn.net/qq_18860653/article/details/80612610