栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybatis源码分析(二)初始化

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybatis源码分析(二)初始化

Mybatis 初始化解析

//将XML配置文件构建为Configuration配置类
//通过加载配置文件流构建一个SqlSessionFactory 解析xml文件
String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

SqlSessionFactoryBuilder.build

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
      try {
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        return build(parser.parse());
      } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
      } finally {
        ErrorContext.instance().reset();
        try {
          reader.close();
        } catch (IOException e) {
          // Intentionally ignore. Prefer previous error.
        }
      }
    }

XMLConfigBuilder.parse 解析成 Configuration

  public Configuration parse() {
    
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    
    parsed = true;
    
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

解析configuration节点 parseConfiguration(parser.evalNode("/configuration"))

private void parseConfiguration(XNode root) {
    try {
      
      propertiesElement(root.evalNode("properties"));
      
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      
      loadCustomVfs(settings);
      
      loadCustomLogImpl(settings);
      
      typeAliasesElement(root.evalNode("typeAliases"));
      
      pluginElement(root.evalNode("plugins"));

      
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));

      // 设置settings 和默认值到configuration
      settingsElement(settings);

      
      environmentsElement(root.evalNode("environments"));
      
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      
      typeHandlerElement(root.evalNode("typeHandlers"));
      
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

解析mappers节点 mapperElement(root.evalNode(“mappers”))

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

解析mapper package 方式
configuration.addMappers(mapperPackage) -> mapperRegistry.addMappers(packageName) -> addMappers(packageName, Object.class)

  public void addMappers(String packageName, Class superType) {
    // 根据包找到所有类
    ResolverUtil> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set>> mapperSet = resolverUtil.getClasses();
    // 循环所有的类
    for (Class mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

解析Mapper过滤

创建一个MapperProxyFactory 把我们的Mapper接口保存到工厂类中, 该工厂用于创建 MapperProxy

public  void addMapper(Class type) {
    
    if (type.isInterface()) {
      
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.    mapper注解构造器
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

解析Mapper

xml解析 loadXmlResource()注解解析 parseStatement(method)

public void parse() {
    String resource = type.toString();
    // 是否已经解析mapper接口对应的xml
    if (!configuration.isResourceLoaded(resource)) {
      // 根据mapper接口名获取 xml文件并解析,  解析里面所有东西放到configuration
      loadXmlResource();
      // 添加已解析的标记
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      // 获取所有方法 看是不是用了注解
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            // 是不是用了注解  用了注解会将注解解析成MappedStatement
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

根据mapper接口名获取xml文件并解析,解析里面所有东西放到configuration
loadXmlResource

private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      // #1347
      InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
      if (inputStream == null) {
        // Search XML mapper that is not in the module but in the classpath.
        try {
          inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
        } catch (IOException e2) {
          // ignore, resource is not required
        }
      }
      if (inputStream != null) {
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }

XMLMapperBuilder.parse

public void parse() {
    
    if (!configuration.isResourceLoaded(resource)) {
      
      configurationElement(parser.evalNode("/mapper"));
      
      configuration.addLoadedResource(resource);

      bindMapperForNamespace();
    }

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

正式解析mapper节点 configurationElement(parser.evalNode("/mapper"))

cache 一级二级缓存

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"));
      
      cacheElement(context.evalNode("cache"));
      
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      
      sqlElement(context.evalNodes("/mapper/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);
    }
  }

插入题cache

  private void cacheElement(XNode context) {
    if (context != null) {
      //解析cache节点的type属性
      String type = context.getStringAttribute("type", "PERPETUAL");
      // 根据别名(或完整限定名)  加载为Class
      Class typeClass = typeAliasRegistry.resolveAlias(type);
      
      String eviction = context.getStringAttribute("eviction", "LRU");
      Class evictionClass = typeAliasRegistry.resolveAlias(eviction);
      //flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
      Long flushInterval = context.getLongAttribute("flushInterval");
      //size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
      Integer size = context.getIntAttribute("size");
      //只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      //把缓存节点加入到Configuration中
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }

解析select | insert |update |delete节点 buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))

XMLStatementBuilderMappedStatement

  private void buildStatementFromContext(List list, String requiredDatabaseId) {
    
    for (XNode context : list) {
      
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

创建MappedStatement对象 XMLStatementBuilder.parseStatementNode()

SqlSource DynamicSqlSource 和 RawSqlSource

  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
    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:  and  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;
    }

    
    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);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }

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

SqlSource SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass)

  public SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType) {
    XMLscriptBuilder builder = new XMLscriptBuilder(configuration, script, parameterType);
    return builder.parsescriptNode();
  }

XMLscriptBuilder.parsescriptNode

  public SqlSource parsescriptNode() {
    
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      // 动态Sql源
      // 动态Sql 就是还需要后续执行时根据传入参数动态解析Sql(因为有等,还要拼接${}sql)
      //    和参数ParameterMappings   也会在后续执行解析,因为动态条件肯定会有动态参数
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      // 静态Sql源  如果没有动态标签(等) 以及 没有${}  就是静态Sql源
      // 静态Sql 就是在这里就解析了Sql  和参数ParameterMappings   后续执行就不用解析了
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    // 其实他们的区别就是动态sql 需要在查询的时候解析 因为有动态sql 和拼接${}
    //                  静态sql 已经在这里确定好sql. 和参数ParameterMapping,
    return sqlSource;
  }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/732898.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号