Mybatis學習系列(四):源碼解析—解析mapper

上一篇主要是介紹了mybatis-config.xml配置文件的解析,裏面只是簡單的列了一下mapper的解析,由於這個是比較核心和重要的,這裏我們將進行詳細的解析:

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

1.package維度的解析

//獲取到整體的包路徑
String mapperPackage = child.getStringAttribute("name");
//調用configuration的方法進行解析和添加Mappers
configuration.addMappers(mapperPackage);

//這裏主要是調用了mapperRegistry的add方法
public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }

//這裏是添加包下的所有mapper類
public void addMappers(String packageName, Class<?> superType) {
    //這裏通過反射拿到包下的所有類的class路徑
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    //循環去解析每個對象
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

1.1我們詳細看一下,單個mapper的解析

public <T> void addMapper(Class<T> type) {
    //當前傳入的class必須是接口
    if (type.isInterface()) {
      //這裏判斷每一個mapper只能被解析一次
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //這裏生成MapperProxyFactory對象並且保存到內存map中
        //key是當前的class類型 values是MapperProxyFactory對象
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        //這裏會初始化MapperAnnotationBuilder對象
        //進行mapper裏的註解的解析(select,insert,update等)
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

從上面已經拿到了解析的對象,下面就是開始對mapper進行解析了

public void parse() {
    String resource = type.toString();
    //每個mapper文件只能被解析一次
    if (!configuration.isResourceLoaded(resource)) {
      //這裏解析mapper文件對應的xml文件
      loadXmlResource();
      //記錄解析過的文件
      configuration.addLoadedResource(resource);
      //設置當前的Namespace
      assistant.setCurrentNamespace(type.getName());
      //解析緩存
      parseCache();
      parseCacheRef();
      //解析當前mapper的方法 生成對應的MappedStatement對象
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    //對解析失敗的方法,重新進行解析一次
    parsePendingMethods();
  }

1.1.1首先來看一下解析對應的mapper.xml文件

private void loadXmlResource() {
    //這裏依然是判斷一下,當前文件是否已經被解析過
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      //組裝xml文件路徑
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      InputStream inputStream = null;
      try {
        inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
      } catch (IOException e) {
        // ignore, resource is not required
      }
      if (inputStream != null) {
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        //解析xml文件
        xmlParser.parse();
      }
    }
  }

解析xml文件

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //解析/mapper標籤
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //綁定mapper和namespace
      bindMapperForNamespace();
    }
    //解析resultMap
    parsePendingResultMaps();
    //解析緩存
    parsePendingCacheRefs();
    //解析綁定Statement
    parsePendingStatements();
  }

1.1.1.1解析mapper標籤

private void configurationElement(XNode context) {
    try {
      //獲取namespace
      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"));
      //解析公共sql
      sqlElement(context.evalNodes("/mapper/sql"));
      //解析(增刪改查)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);
    }
  }

1.1.1.1.1我們看下這裏的解析入參映射

private void parameterMapElement(List<XNode> list) throws Exception {
    //循環所有的入參定義
    for (XNode parameterMapNode : list) {
      //定義的ID
      String id = parameterMapNode.getStringAttribute("id");
      //對應的對象類型
      String type = parameterMapNode.getStringAttribute("type");
      //獲取當前對象class
      Class<?> parameterClass = resolveClass(type);
      //解析遍歷當前定義的所有的參數字段
      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
      List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
      for (XNode parameterNode : parameterNodes) {
        //參數字段
        String property = parameterNode.getStringAttribute("property");
        //對應的java類型
        String javaType = parameterNode.getStringAttribute("javaType");
        //對應的數據庫類中
        String jdbcType = parameterNode.getStringAttribute("jdbcType");
        //返回的map
        String resultMap = parameterNode.getStringAttribute("resultMap");
        String mode = parameterNode.getStringAttribute("mode");
        //類型轉換的轉換器
        String typeHandler = parameterNode.getStringAttribute("typeHandler");
        Integer numericScale = parameterNode.getIntAttribute("numericScale");
        ParameterMode modeEnum = resolveParameterMode(mode);
        Class<?> javaTypeClass = resolveClass(javaType);
        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
        @SuppressWarnings("unchecked")
        Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
        //綁定關聯關係
        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
        parameterMappings.add(parameterMapping);
      }
      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
    }
  }

1.1.1.1.2解析結果映射

private void resultMapElements(List<XNode> list) throws Exception {
    //循環所有的映射設定
    for (XNode resultMapNode : list) {
      try {
        //解析單個映射設定
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
  }

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    //獲取ID
    String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
    //獲取返回類型
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
    String extend = resultMapNode.getStringAttribute("extends");
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    Class<?> typeClass = resolveClass(type);
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    resultMappings.addAll(additionalResultMappings);
    List<XNode> resultChildren = resultMapNode.getChildren();
    //解析所有字段
    for (XNode resultChild : resultChildren) {
      //如果是構造方法形式的
      if ("constructor".equals(resultChild.getName())) {
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        List<ResultFlag> flags = new ArrayList<ResultFlag>();
        //如果是主鍵 多了這個操作
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        //組裝ResultMapping關係
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    //組裝處理對象
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      //進行類型轉換處理
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }

類型轉換處理  最終其實是保存到configuration的ResultMap中

public ResultMap resolve() {
    //調用MapperBuilderAssistant方法
    return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
  }


public ResultMap addResultMap(
      String id,
      Class<?> type,
      String extend,
      Discriminator discriminator,
      List<ResultMapping> resultMappings,
      Boolean autoMapping) {
    //獲取當前定義的ID
    id = applyCurrentNamespace(id, false);
    //獲取引入的其他定義的ID
    extend = applyCurrentNamespace(extend, true);

    //引入的不爲空
    if (extend != null) {
      if (!configuration.hasResultMap(extend)) {
        throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
      }
      //拿到引入解析後的ResultMap映射關係
      ResultMap resultMap = configuration.getResultMap(extend);
      List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
      //引入的映射關係  刪除掉當前ID下的映射關係
      extendedResultMappings.removeAll(resultMappings);
      //如果當前ID下配置的是構造方法類型的 刪除引入裏面的構造方法的映射
      boolean declaresConstructor = false;
      for (ResultMapping resultMapping : resultMappings) {
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          declaresConstructor = true;
          break;
        }
      }
      if (declaresConstructor) {
        Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
        while (extendedResultMappingsIter.hasNext()) {
          if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
            extendedResultMappingsIter.remove();
          }
        }
      }
      //把引入的映射關係添加到當前的映射關係裏
      resultMappings.addAll(extendedResultMappings);
    }
    //組裝型的映射關係對象 添加到configuration中
    ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
        .discriminator(discriminator)
        .build();
    configuration.addResultMap(resultMap);
    return resultMap;
  }

1.1.1.1.3解析公共sql

private void sqlElement(List<XNode> list) throws Exception {
    //如果有DatabaseId 先解析一遍SQL
    if (configuration.getDatabaseId() != null) {
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }

private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    //循環所有的SQL定義
    for (XNode context : list) {
      String databaseId = context.getStringAttribute("databaseId");
      //獲取當前定義的ID
      String id = context.getStringAttribute("id");
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        //這裏比較簡單  就是保存ID和內容的映射關係
        sqlFragments.put(id, context);
      }
    }
  }

1.1.1.1.4解析(增刪改查)SQL

private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    //循環所有的sql定義
    for (XNode context : list) {
      //組裝解析器
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        //解析sql定義
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

解析每一個單獨的SQL定義

public void parseStatementNode() {
    //獲取當前sql的定義ID
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    //獲取一些配置的參數  包括入參,出參等
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    //獲取sql的類型(增刪改查)
    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也就是引入了其他的定義(公共SQL)
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // 解析selectKey 標籤
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    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;
    }
    //創建MappedStatement
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

創建MappedStatement:調用的是MapperBuilderAssistant的方法

public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }
    //生成新的ID 爲namespace.配置的ID
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    //創建MappedStatement 
    MappedStatement statement = statementBuilder.build();
    //加入到configuration中的map裏 以ID爲key MappedStatement 爲值
    configuration.addMappedStatement(statement);
    return statement;
  }

1.1.1.2綁定mapper和namespace,這裏最終還是走到了configuration.addMapper(boundType),也就是我們上面的1.1

private void bindMapperForNamespace() {
    //獲取當前的namespace
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      //轉換成class對象
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        //當前mapper是否解析過
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          //解析mapper添加到configuration中
          configuration.addMapper(boundType);
        }
      }
    }
  }

2.resource和url維度的解析:最終走到的是XMLMapperBuilder 的parse()方法,也就是我們上面的1.1.1的第二段代碼

if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            //解析resource 配置的路徑xml
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //解析url 配置的路徑xml
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          }

3.單個mapper配置維度解析:調用的還是我們的上面的1.1步奏

else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          }

到這裏mapper解析就完成了,最終把每個mapper類中的方法生成了一個個的MappedStatement 對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章