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
具体执行的时候,由此为起点。

猜你喜欢

转载自blog.csdn.net/jsh_941112341/article/details/84850594