Mybatis配置解析 --构建者模式

288 阅读6分钟

Mybatis的构建者模式

  • 这些构建者都是为mybatis加载资源,解析mybatis-config.xml、*mapper.xml、mapper.class的

  • 按建造者的编写顺序,也是mybatis资源的加载顺序

  • mybatis解析资源时核心代码调用链

    SqlSessionFactoryBuilder#build (构建SqlSessionFactory)

    →XMLConfigBuilder#parseConfiguration (解析Mybatis-Config.xml)

    →XMLConfigBuilder#mapperElement(解析Mybatis-Config.xml中标签)

    →MapperAnnotationBuilder#parse (解析某一个mapper.class)

    →XMLMapperBuilder#parse (解析某一个mapper.xml+解析挂起的结果集映射+解析挂起的缓存空间引用+解析挂起的Statements)

    →XMLMapperBuilder#configurationElement (解析某一个mapper.xml)

    →CacheBuilder#build (构建二级缓存责任链)

    →CacheBuilder#setStandardDecorators (按需(读配置)设置基准责任链)

    →XMLStatementBuilder#parseStatementNode (解析单个select|insert|update|delete标签)

    →XMLScriptBuilder#parseScriptNode (解析sql语句,非动态sql,将#{},转换成?)

  • 代表

    • SqlSessionFactoryBuilder
    • XMLConfigBuilder
    • MapperAnnotationBuilder
    • XMLMapperBuilder
    • CacheBuilder
    • XMLStatementBuilder
    • XMLScriptBuilder

    待理清楚

    • parsePendingResultMaps

      resultMap的extends的没有解析,等待其余resultMap都解析完毕再次调用ResultMapResolver的resolve方法解析。此时若从configuration还得不到id为extends的ResultMap则忽略,因为parsePendingResultMaps中捕获了IncompleteElementException并啥也不做。

    • parsePendingCacheRefs

      refId引用的还未解析缓存下来,等到解析完毕再次解析。

    • parsePendingStatements

      Cache-ref未解析完被耽误的MappedStatement再次解析。

SqlSessionFactoryBuilder

作用:屏蔽复杂构建过程,使客户端能轻松的获得SqlSessionFactory 对象

构建流程

  1. SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,构建Mybatis运行的核心对象Configuration对象
  2. 然后将该Configuration对象作为参数构建一个SqlSessionFactory对象。
//SqlSessionFactoryBuilder#build(InputStream inputStream, String environment, Properties properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
			//1.SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的
			//MybatisMapConfig.xml和所有的*Mapper.xml文件
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //2.构建Mybatis运行的核心对象Configuration对象 <-(parser.parse())
			//3.该Configuration对象作为参数构建一个SqlSessionFactory对象<-build(parser.parse());
			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.
      }
    }
  }

public SqlSessionFactory build(Configuration config) {
	//该Configuration对象作为参数构建一个SqlSessionFactory对象
    return new DefaultSqlSessionFactory(config);
  }

XMLConfigBuilder

作用:解析Mybatis-Config.xml,生成Configuration对象

XMLConfigBuilder#parseConfiguration


//org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
//作用:
//1.解析Mybatis-Config.xml
//2.解析配置
public Configuration parse() {
		//1.校验是否已经解析过配置
		//parsed:是否解析过配置的标识,防止重复解析,浪费性能
		//parsed会在XMLConfigBuilder的构建方法中默认复制为false
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
			//2.解析配置
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
//XMLConfigBuilder#parseConfiguration
//解析配置
private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
			//1.解析<properties/>
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
			//2.解析<settings/>
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
			//3.解析<typeAliases/>
      typeAliasesElement(root.evalNode("typeAliases"));
			//4.解析<plugins/>
      pluginElement(root.evalNode("plugins"));
			//5.解析<objectFactory/>
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
			//<settings/>标签赋值
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
			//6.解析<environments/>
      environmentsElement(root.evalNode("environments"));
			//7.解析<databaseIdProvider/>数据库厂商标识,支持是基于映射语句中的 databaseId 属性
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
			//8.解析<typeHandlers/>类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
			//9.解析<mappers>映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

XMLConfigBuilder#mapperElement

//解析<mappers/>映射器
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
			/*parent:
				<mappers>
					<mapper class="com.xxx.xxx1Dao"/>
					<mapper class="com.xxx.xxx2Dao"/>
				</mappers>
			*/
      for (XNode child : parent.getChildren()) {
				//1.单个单个的解析 <mapper class="com.xxx.xxx1Dao"/>
        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.");
          }
        }
      }
    }
  }

XMLConfigBuilder在构建Configuration对象时,也会调用XMLMapperBuilder用于读取*Mapper 文件

MapperAnnotationBuilder

作用:解析某一个mapper.class

MapperAnnotationBuilder#parse

路径:XMLConfigBuilder#mapperElement→Configuration#addMapper→MapperRegistry#addMapper

→MapperAnnotationBuilder#parse

//org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
public void parse() {
		//type是Mapper.class
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
			//解析mapper.xml,调用XMLMapperBuilder!!!!!!!
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
			//解析注解@CacheNamespace
      parseCache();
			//解析注解@CacheNamespaceRef
      parseCacheRef();
			//解析Mapper.class的所有方法
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
					//解析CacheNamespaceRef
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

XMLMapperBuilder

作用:解析某一个mapper.xml

XMLMapperBuilder#parse

路径:MapperAnnotationBuilder#parse→MapperAnnotationBuilder#loadXmlResource

→XMLMapperBuilder#parse

public void parse() {
		//resource:即mapper.xml
    if (!configuration.isResourceLoaded(resource)) {//避免重复解析
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);//存入Set,表明已经解析过了
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

XMLMapperBuilder#configurationElement

//XMLMapperBuilder#configurationElement
//context 某个mapper.xml文件
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"));
		//1.解析<cache/>标签,调用***CacheBuilder***
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
		//2.解析一堆<select><insert><update><delete>标签,挨个调用**XMLMapperBuilder**
    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);
  }
}

XMLMapperBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句

XMLMapperBuilder会使用CacheBuilder来读取和build所有的Cache

CacheBuilder

作用:解析@CacheNamespace或标签,同时生成二级缓存责任链

CacheBuilder#build

路径-注解:MapperAnnotationBuilder#parse→MapperAnnotationBuilder→parseCache

→MapperBuilderAssistant#useNewCache→CacheBuilder#build

路径-xml:XMLMapperBuilder#configurationElement→XMLMapperBuilder#cacheElement

→MapperBuilderAssistant#useNewCache→CacheBuilder#build

public Cache build() {
		//1.声明默认(未配置的情况下)的缓存责任链,PerpetualCache和LruCache
    setDefaultImplementations();
		//生成Cache,未配置默认情况是生成PerpetualCache
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
    if (PerpetualCache.class.equals(cache.getClass())) {
			//默认情况下是LruCache
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
			//默认情况下,生成基准缓存责任链
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
    }
    return cache;
  }

CacheBuilder#setStandardDecorators

//按需生成基准缓存责任链
private Cache setStandardDecorators(Cache cache) {
    try {
      MetaObject metaCache = SystemMetaObject.forObject(cache);
      if (size != null && metaCache.hasSetter("size")) {
        metaCache.setValue("size", size);
      }
			//clearInterval 配置中读取
      if (clearInterval != null) {
        cache = new ScheduledCache(cache);
        ((ScheduledCache) cache).setClearInterval(clearInterval);
      }
			//配置中读取,默认true(应该是)
      if (readWrite) {
        cache = new SerializedCache(cache);
      }
      cache = new LoggingCache(cache);
      cache = new SynchronizedCache(cache);
			//配置中读取
      if (blocking) {
        cache = new BlockingCache(cache);
      }
      return cache;
    } catch (Exception e) {
      throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
  }

XMLStatementBuilder

作用:解析单个<select><insert><update><delete>标签

XMLStatementBuilder#parseStatementNode

路径:XMLMapperBuilder#buildStatementFromContext→XMLStatementBuilder#parseStatementNode

//解析单个<select><insert><update><delete>标签
//1.解析标签内的sql语句,调用XMLScriptBuilder#parseScriptNode
//2.其他都是解析标签中的属性值
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<?> 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: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
		//1.此处解析标签内的sql语句,调用XMLScriptBuilder#parseScriptNode
		//2.其他都是解析标签中的属性值
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

XMLScriptBuilder

作用:解析sql语句,非动态sql,将#{},转换成?

XMLScriptBuilder#parseScriptNode

路径:XMLStatementBuilder#parseStatementNode→LanguageDriver#createSqlSource

→XMLLanguageDriver#createSqlSource→XMLScriptBuilder#parseScriptNode

public SqlSource parseScriptNode() {
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
			//0.没啥处理的,应该是动态sql执行的时候才解析
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
			//1.将sql中的#{},转换成?
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

总结

  • Mybatis是使用这些构建者来做环境初始化的

  • 所有的建造者设计都是为了复杂对象,客户构建简单

  • 建造者核心步骤调用链(省略的路径看上面写的对应的核心方法)

    SqlSessionFactoryBuilder#build (构建SqlSessionFactory)

    →XMLConfigBuilder#parseConfiguration (解析Mybatis-Config.xml)

    →XMLConfigBuilder#mapperElement(解析Mybatis-Config.xml中标签)

    →MapperAnnotationBuilder#parse (解析某一个mapper.class)

    →XMLMapperBuilder#parse (解析某一个mapper.xml+解析挂起的结果集映射+解析挂起的缓存空间引用+解析挂起的Statements)

    →XMLMapperBuilder#configurationElement (解析某一个mapper.xml)

    →CacheBuilder#build (构建二级缓存责任链)

    →CacheBuilder#setStandardDecorators (按需(读配置)设置基准责任链)

    →XMLStatementBuilder#parseStatementNode (解析单个select|insert|update|delete标签)

    →XMLScriptBuilder#parseScriptNode (解析sql语句,非动态sql,将#{},转换成?)

  • 这些构建者的核心方法

    • SqlSessionFactoryBuilder#build(Configuration config)
    • XMLConfigBuilder#parseConfiguration
    • XMLConfigBuilder#mapperElement
    • MapperAnnotationBuilder#parse
    • XMLMapperBuilder#parse
    • XMLMapperBuilder#configurationElement
    • CacheBuilder#build
    • CacheBuilder#setStandardDecorators
    • XMLStatementBuilder#parseStatementNode
    • XMLScriptBuilder#parseScriptNode
  • 几个构造者的包路径

    • org.apache.ibatis.session.SqlSessionFactoryBuilder
    • org.apache.ibatis.builder.xml.XMLConfigBuilder
    • org.apache.ibatis.builder.annotation.MapperAnnotationBuilder
    • org.apache.ibatis.builder.xml.XMLMapperBuilder
    • org.apache.ibatis.builder.xml.XMLStatementBuilder
    • org.apache.ibatis.mapping.CacheBuilder
    • org.apache.ibatis.scripting.xmltags.XMLScriptBuilder