Mybatis源码细节探究:二级缓存Cache对象是在什么时候创建的?

简介: Mybatis源码细节探究:二级缓存Cache对象是在什么时候创建的?


给自己的每日一句

不从恶人的计谋,不站罪人的道路,不坐亵慢人的座位,惟喜爱耶和华的律法,昼夜思想,这人便为有福!他要像一棵树栽在溪水旁,按时候结果子,叶子也不枯干。凡他所做的尽都顺利

本文内容整理自《孙哥说Mybatis系列课程》

Cache是在什么时候、什么地点被创建的?

不管是我们使用Mybatis写测试程序,还是在真实企业级项目中使用Mybatis,Mybatis框架被启动加载的前几行代码一定是如下:

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

然后我们的二级缓存Cache对象就是在这个期间,也就是Mybatis被启动和初始化的时候完成的,那么我们就详细展开吧。

首先:我要告诉大家的是,这个过程一定是在SqlSessionFactory工厂被创建的时候完成的,那就是上述的第二行代码,并且这里应用的是构建者设计模式,这个精华必须隐藏在build()方法中,我们查看一下:

public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  //重载的build方法,真正的精华是在这里
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //将Mybatis和核心配置文件读取成inputStream之后,我们有将他转换成Mybatis中重要的
      //XMLConfigBuilder对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      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.
      }
    }
  }

return build(parser.parse());这里边硬货就多了,build方法就是为了创建

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

所以,里边的parser.parse()的返回值一定是生成的Configuration对象,原材料就是Mybatis核心配置文件读取到的InputStream,有因为InputStream当中记录了Mapper.xml的路径,所以这些Mapper文件必定也是在这里边解析的。

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

parser.evalNode(“/configuration”)兄弟们,这玩意用脚趾头都知道,这读的是mybatis-config.xml里边的Configuration标签,这样就把mybatis-config.xml整个文件作为一个Node对象被放到了parseConfiguration方法当中,这个方法的目的从方法上就能看出来,为了构建Configuration对象

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

兄弟们,眼熟了吧,这不就是mybatis-config.xml里边的一个一个的标签么,一个一个的排队解析,看最后一行,这玩意里边存储的不就是Mapper标签么,所以Mapper.xml必定就是在这里边解析的,再加上我们知道Cache标签是写在Mapper.xml里边的,所以Cache对象必是在里边创建的。

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.");
          }
        }
      }
    }
  }

兄弟们,XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());这玩意不就是在读Mapper.xml文件么,mapperParser.parse();又去parse去了,我们进去看看

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

configurationElement兄弟们,看到名字还不明白么,这是在解析Mapper.xml当中的元素解析到Configurantion对象当中呀,Cache标签也是其中的一个元素呀

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }
private void cacheElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type", "PERPETUAL");
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      String eviction = context.getStringAttribute("eviction", "LRU");
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      Long flushInterval = context.getLongAttribute("flushInterval");
      Integer size = context.getIntAttribute("size");
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }

兄弟们,到这里基本上知道在哪里创建的吧?至于怎么创建,咱们后期出文章玩~

相关文章
|
4天前
|
存储 缓存 监控
中间件Read-Through Cache(直读缓存)策略实现方式
【5月更文挑战第11天】中间件Read-Through Cache(直读缓存)策略实现方式
13 4
中间件Read-Through Cache(直读缓存)策略实现方式
|
4天前
|
存储 缓存 监控
中间件Read-Through Cache(直读缓存)策略注意事项
【5月更文挑战第11天】中间件Read-Through Cache(直读缓存)策略注意事项
9 2
|
4天前
|
存储 缓存 中间件
中间件Read-Through Cache(直读缓存)策略工作原理
【5月更文挑战第11天】中间件Read-Through Cache(直读缓存)策略工作原理
11 3
|
6天前
|
缓存 中间件 数据库
中间件Write-Through Cache(直写缓存)策略
【5月更文挑战第7天】中间件Write-Through Cache(直写缓存)策略
17 4
中间件Write-Through Cache(直写缓存)策略
|
6天前
|
存储 缓存 中间件
中间件Read-Through Cache(直读缓存)策略
【5月更文挑战第7天】中间件Read-Through Cache(直读缓存)策略
16 4
中间件Read-Through Cache(直读缓存)策略
|
6天前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
6天前
|
SQL 缓存 Java
|
6天前
|
缓存 小程序
uniapp读取(获取)缓存中的对象值(微信小程序)
uniapp读取(获取)缓存中的对象值(微信小程序)
16 1
|
6天前
|
缓存 NoSQL Java
缓存框架-Spring Cache的使用
Spring Cache是一个注解驱动的缓存框架,它提供了一层抽象,允许切换不同的缓存实现,如EHCache、Caffeine和Redis。启用缓存只需在配置中引入相关依赖并开启`@EnableCaching`。`@Cacheable`用于方法执行前检查缓存,存在则直接返回,不存在则执行方法并将结果存入缓存。`@CachePut`在方法执行后将结果放入缓存,常用于更新操作。`@CacheEvict`用于清除缓存数据,可以按key删除或清空整个缓存。`@Caching`可以组合多个缓存操作。在Redis中,可以通过序列化处理存储复杂对象,提高可读性。
63 4
|
6天前
|
XML 缓存 Java
MyBatis二级缓存解密:深入探究缓存机制与应用场景
MyBatis二级缓存解密:深入探究缓存机制与应用场景
82 2
MyBatis二级缓存解密:深入探究缓存机制与应用场景