Mybatis 初始化解析
//将XML配置文件构建为Configuration配置类//通过加载配置文件流构建一个SqlSessionFactory 解析xml文件String resource = "mybatis-config.xml";Reader reader = Resources.getResourceAsReader(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactoryBuilder.build
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore、Prefer previous error. } } }
XMLConfigBuilder.parse 解析成 Configuration
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; }
解析configuration节点 parseConfiguration(parser.evalNode("/configuration"))
private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); // 设置settings 和默认值到configuration settingsElement(settings); 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); } }
解析mappers节点 mapperElement(root.evalNode(“mappers”))
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 package 方式
configuration.addMappers(mapperPackage) -> mapperRegistry.addMappers(packageName) -> addMappers(packageName, Object.class)
public void addMappers(String packageName, Class<?> superType) { // 根据包找到所有类 ResolverUtil
解析Mapper过滤
创建一个MapperProxyFactory 把我们的Mapper接口保存到工厂类中, 该工厂用于创建 MapperProxy
public
解析Mapper
xml解析 loadXmlResource()注解解析 parseStatement(method)
public void parse() { String resource = type.toString(); // 是否已经解析mapper接口对应的xml if (!configuration.isResourceLoaded(resource)) { // 根据mapper接口名获取 xml文件并解析, 解析
根据mapper接口名获取xml文件并解析,解析里面所有东西放到configuration
loadXmlResource
private void loadXmlResource() { // Spring may not know the real resource name so we check a flag // to prevent loading again a resource twice // this flag is set at XMLMapperBuilder#bindMapperForNamespace if (!configuration.isResourceLoaded("namespace:" + type.getName())) { String xmlResource = type.getName().replace('.', '/') + ".xml"; // #1347 InputStream inputStream = type.getResourceAsStream("/" + xmlResource); if (inputStream == null) { // Search XML mapper that is not in the module but in the classpath. try { inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource); } catch (IOException e2) { // ignore, resource is not required } } if (inputStream != null) { XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); xmlParser.parse(); } } }
XMLMapperBuilder.parse
public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
正式解析mapper节点 configurationElement(parser.evalNode("/mapper"))
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); } }
插入题cache
private void cacheElement(XNode context) { if (context != null) { //解析cache节点的type属性 String type = context.getStringAttribute("type", "PERPETUAL"); // 根据别名(或完整限定名) 加载为Class Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); String eviction = context.getStringAttribute("eviction", "LRU"); Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); //flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。 Long flushInterval = context.getLongAttribute("flushInterval"); //size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。 Integer size = context.getIntAttribute("size"); //只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false boolean readWrite = !context.getBooleanAttribute("readOnly", false); boolean blocking = context.getBooleanAttribute("blocking", false); Properties props = context.getChildrenAsProperties(); //把缓存节点加入到Configuration中 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } }
解析select | insert |update |delete节点 buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))
XMLStatementBuilderMappedStatement
private void buildStatementFromContext(List
创建MappedStatement对象 XMLStatementBuilder.parseStatementNode()
SqlSource DynamicSqlSource 和 RawSqlSource
public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); String parameterType = context.getStringAttribute("parameterType"); // 把参数类型字符串转化为class Class<?> parameterTypeClass = resolveClass(parameterType); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre:
SqlSource SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass)
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLscriptBuilder builder = new XMLscriptBuilder(configuration, script, parameterType); return builder.parsescriptNode(); }
XMLscriptBuilder.parsescriptNode
public SqlSource parsescriptNode() { MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { // 动态Sql源 // 动态Sql 就是还需要后续执行时根据传入参数动态解析Sql(因为有