1. 程式人生 > >mybatis-xml解析

mybatis-xml解析

實習日誌(1)mybatis原始碼-xml解析

流程

在這裡插入圖片描述

 public static void main(String[] args) throws IOException {
        File file = Resources.getResourceAsFile("mybatis-config.xml");
        InputStream is = new FileInputStream(file);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        try (SqlSession session = sqlSessionFactory.openSession()) {
            RoleInfoMapper roleInfoMapper = session.getMapper(RoleInfoMapper.class);
            List<RoleInfo> roleInfoList = roleInfoMapper.selectAll();
            session.commit();
        }
  }      

大致流程如上。

1.xml解析 裝配configuration檔案

SqlSessionFactoryBuilder().build(is);
方法實際上:

關鍵程式碼:
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
   
  .............省略異常處理............
 public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

關鍵在於parse方法,解析完成了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")); // 完成 configuration 的  typeAlises初始化
      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")); // 這是最重要的  向mapperRegistry註冊,mapperRegistry維護一個map<Class,
      // MapperProxyFactory>
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

可以配合著官方文件一起看,我們以解析typeAlisesElement為例,負責解析typeAlises標籤

  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
// TypeAliseRegistry 內部維護一個hashmap<String, Class<?>> ,並在初始化的時候就往裡添加了
// string- String.class 
// 通過定義的resloveAlias(String) 可以根據別名返回型別

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {  
        // -- 先不看
        } else {
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              typeAliasRegistry.registerAlias(clazz); //  沒有alias 會去找註解,註解沒有預設首字母小寫
            } else {
              typeAliasRegistry.registerAlias(alias, clazz); // 加入map
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }
另外一個比較重要的是typeHandler,解析流程幾乎是一樣的
讀取標籤,註冊到configuration的typeHandlerRegistry 物件中。
只是typeHandlerRegistry稍稍複雜一些:
 private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<>();
  private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<>();
  // 總而言之,維護著 java 型別   -> JDBC 型別  -> 具體實TypeHandler類  和
  // TypeHandler.Class  -> TypeHandler物件 
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
      if (map == null || map == NULL_TYPE_HANDLER_MAP) {
        map = new HashMap<JdbcType, TypeHandler<?>>();
        TYPE_HANDLER_MAP.put(javaType, map);
      }
      map.put(jdbcType, handler);
    }
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }

在 paramerterHandler 負責設定引數 的 時候 就使用了從parameterMapping中取得的 typeHandler,最終的具體設定都交給了typeHandler去執行,同樣,結果集的結果處理也是一樣的

2.mapper對映檔案處理

解析mybatis-config的最後一步就是解析mapper檔案,解析完會向configuration註冊mapper.class - 和能夠建立mapper代理物件的工廠。對mapper檔案的解析會呼叫 另外一個XMLMapperBuilder來進行解析,關鍵程式碼:

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");
      } // 先處理cache-ref再處理cache
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析resultMap 重中之重
      sqlElement(context.evalNodes("/mapper/sql")); // 這裡要完成sql的解析,放入一個map<string,sqlNode>
      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,resultMap,sql, update|select|insert|delete
cache先放一放,
重點看一下resultMap的解析和增刪該查的解析:

 private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType")))); // 屬性resultType javaType  ofType type一個意思
    String extend = resultMapNode.getStringAttribute("extends");
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 會覆蓋全域性,預設unset
    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>(); //  封裝每一個result物件
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); // 會完成一個result或者id標籤的所有屬性解析工作
      }
    }
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }

完成後同樣要向configuration註冊,在一個Map<String, ResultMap>,
被使用是在defaultResultSetHandler中 ,用於建立結果物件

增刪該查標籤的處理 與之類似,最終會註冊到configuration的物件中
Map<String, MappedStatement> mappedStatements
具體執行的時候,由此為起點。