Spring mybatis源码篇章-MybatisDAO文件解析

http://www.cnblogs.com/question-sky/p/6612604.html


默认加载mybatis主文件方式

XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      if (this.logger.isDebugEnabled()) {
        this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
      }
      //sqlSessionFactoryBean不指定configLocation属性则采用默认的Configuration对象
      configuration = new Configuration();
      configuration.setVariables(this.configurationProperties);
    }

简单的看下Configuration的无参数构造函数

public Configuration() {
    //预存常用的别名
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

简单的发现其并不去设置前一章节的mybatis主文件中的相关属性,比如settings、environment等

mapper sql 配置文件的加载

前提是sqlSessionFactoryBean设置了mapperLocations属性,比如<property name="mapperLocations" value="classpath:com/du/wxServer/mapper/*.xml" />

  1. 查看sqlSessionFactoryBean#buildSqlSessionFactory()方法

    if (!isEmpty(this.mapperLocations)) {
      //具体的如何从string转为Resource[],暂且不知何处加载获得,有兴趣的读者可补充
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }
    
        try {
         //对扫描包及其子包下的每个sql mapper配置文件进行解析
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }
    
        if (this.logger.isDebugEnabled()) {
          this.logger.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (this.logger.isDebugEnabled()) {
        this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }
  2. 直接进入XMLMapperBuilder#parse方法

    public void parse() {
    //对每个xml资源只加载一次
    if (!configuration.isResourceLoaded(resource)) {
      //解析xml配置,其中配置的根节点必须为mapper
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //绑定mapper的工作区间
      bindMapperForNamespace();
    }
    
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
      }
  3. 分析XMLMapperBuilder#configurationElement方法

    try {
      //表明mapper根节点的namespace属性是必须的,且不为空
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
          throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //设置工作区间
      builderAssistant.setCurrentNamespace(namespace);
      //解析相应的属性
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      //解析<parameterMap>节点集合
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析<resultMap>节点集合
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析<sql>节点集合
      sqlElement(context.evalNodes("/mapper/sql"));
      //创建MappedStatement,这里与注解方式的加载方式还是类似的      
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
    这里讨论下与前一章节不同的解析同一属性配置方法:
    • XMLMapperBuilder#resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings)解析单个<resultMap>节点方法

      ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
      //读取id属性,最好配置以免不必要的错误
      String id = resultMapNode.getStringAttribute("id",
      resultMapNode.getValueBasedIdentifier());
      //优先级为type>ofType>resultType>javaType
      String type = resultMapNode.getStringAttribute("type",
      resultMapNode.getStringAttribute("ofType",
      resultMapNode.getStringAttribute("resultType",
      resultMapNode.getStringAttribute("javaType"))));
      
      String extend = resultMapNode.getStringAttribute("extends");
      //是否开启自动映射,默认值为unset
      Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
      Class<?> typeClass = resolveClass(type);
      
      //<discriminator><case /><case/></discriminator>根据结果值进行结果类型的映射,类似java的switch-case语法 
      Discriminator discriminator = null;
      //ResultMap节点信息转化为ResultMapping集合
      List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
      resultMappings.addAll(additionalResultMappings);
      List<XNode> resultChildren = resultMapNode.getChildren();
      for (XNode resultChild : resultChildren) {
        if ("constructor".equals(resultChild.getName())) {
          //<resultMap>节点下<constructor>节点处理
          processConstructorElement(resultChild, typeClass, resultMappings);
        } else if ("discriminator".equals(resultChild.getName())) {
          //<resultMap>节点下<discriminator>节点处理
          discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
        } else {
          //<id>/<result>/<collection>/<association>节点的解析
          ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
          if ("id".equals(resultChild.getName())) {
            flags.add(ResultFlag.ID);
          }
          resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
        }
      }
      ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
      try {
        //组装成ResultMap对象保存到Configuration对象的私有集合变量resultMaps
        return resultMapResolver.resolve();
      } catch (IncompleteElementException  e) {
        configuration.addIncompleteResultMap(resultMapResolver);
        throw e;
      }
    • XMLMapperBuilder#sqlElement(List<XNode list>)sql节点信息的解析,主要作用是将每个sql节点对象都保存到Configuration对象中的Map<String, XNode> sqlFragments属性中。

      for (XNode context : list) {
        //sql节点的databaseId属性
        String databaseId = context.getStringAttribute("databaseId");
        //sql节点的id属性
        String id = context.getStringAttribute("id");
        //id=${namespace}+"."+id
        id = builderAssistant.applyCurrentNamespace(id, false);
        //true的前提是
        //主配置文件指定了databaseId属性
        //或者主配置和sql节点的databaseId属性均不存在,但sql节点的id属性存在
        if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
      sqlFragments.put(id, context);
        }
      }
    • XMLMapperBuilder#buildStatementFromContext(context.evalNodes("select|insert|update|delete"))CRUD语句节点解析,
      这里直接看 XMLStatementBuilder#parseStatementNode()方法的部分代码

      //节点上支持的常见属性
      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);
      ...
      String resultSetType = context.getStringAttribute("resultSetType");
      StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
      ...
      // Include Fragments before parsing 导入<include>标签内容,其内部可以含有<if>/<where>/<set>等标签
      XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
      includeParser.applyIncludes(context.getNode());
      
      
      // Parse selectKey after includes and remove them.导入<selectKey>标签内容
      processSelectKeyNodes(id, parameterTypeClass, langDriver);
      
      // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) 如何解析sql语句?放置下一章节讲解
      SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
      
      ...
      //这里就跟上一章节的注解生成MappedStatement是一致的,最终都是保存在Configuration的Map<String, MappedStatement> mappedStatement集合属性
      builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered, 
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

总结

  • 不管是通过注解模式还是配置文件模式,都会生成MappedStatement对象保存到Configuration对象中
  • 注解模式可以很好的直接通过上一章节的讲解模式来达到sql语句与类直接绑定;但本章的sql 配置文件并没有讲到如何绑定对应namespace指向的class对象。这在MapperScannerConfigurer源码分析中讲解
  • 每个select|update|insert|delete标签均会被解析为单个MappedStatement对象,其中的id为namespace_id作为唯一标志

下节预告

Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值