给自己的每日一句
不从恶人的计谋,不站罪人的道路,不坐亵慢人的座位,惟喜爱耶和华的律法,昼夜思想,这人便为有福!他要像一棵树栽在溪水旁,按时候结果子,叶子也不枯干。凡他所做的尽都顺利
本文内容整理自《孙哥说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); } }
兄弟们,到这里基本上知道在哪里创建的吧?至于怎么创建,咱们后期出文章玩~