mybatis流程解析(2)

看下面SqlSessionFactory是怎么构建的

    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

在这里插入图片描述
通过XMLConfigBuilder去解析,看看XMLConfigBuilder怎么初始化的

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

发现做了两件事情:
1.构建MyBatis DTD的离线实体解析器,就是XMLMapperEntityResolver
2.构建XPathParser

 public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(reader));创建document
  }

commonConstructor方法

 private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation; 是否开启验证默认是true
    this.entityResolver = entityResolver;离线实体解析器
    this.variables = variables;配置文件里面Properties节点的内容
    XPathFactory factory = XPathFactory.newInstance();获取xpath工厂
    this.xpath = factory.newXPath();实例化xpath
  }

XMLConfigBuilder的重载方法

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());初始化父类的Configuration
    ErrorContext.instance().resource("SQL Mapper Configuration");记录上下文
    this.configuration.setVariables(props);//设置父类的Configuration的变量
    this.parsed = false;设置变量为没解析
    this.environment = environment;设置环境变量
    this.parser = parser;设置XPathParser
  }

初始化父类的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);
  }

至此XMLConfigBuilder初始化完毕,总结下

  1. 设置了是否解析变量为false
  2. 构建XPathParser(里面创建了xpath,还有document等等。。。)
  3. 设置了环境变量environment
  4. 构建localReflectorFactory
  5. 构建configuration
  6. 复制configuration内的typeAliasRegistry和typeHandlerRegistry到XMLConfigBuilder

XMLConfigBuilder的parse流程

public Configuration parse() {
    //判断xml是否解析过解析过就抛异常
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //设置xml已解析
    parsed = true;
    //解析xml下的configuration节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      // 读取 properties的属性
      propertiesElement(root.evalNode("properties"));
      // 读取 settings 属性
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      // 从settings指定 VFS 的实现
      loadCustomVfs(settings);
      // 从settings指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
      loadCustomLogImpl(settings);
      //解析 类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      // 解析 插件
      pluginElement(root.evalNode("plugins"));
      // 设置对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //设置 settings
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 环境配置
      environmentsElement(root.evalNode("environments"));
      // 数据库厂商标识
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 映射器(mappers)
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

读取 properties的属性

 private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      // 获取所有子节点的name、value属性键值对
      Properties defaults = context.getChildrenAsProperties();
      //获取resource属性
      String resource = context.getStringAttribute("resource");
      //获取url属性
      String url = context.getStringAttribute("url");
      //如果两个都不为null 就抛出异常
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      //解析resource或者url放进Properties里面
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //从configuration获取变量
      Properties vars = configuration.getVariables();
      //不为空就加入Properties里面
      if (vars != null) {
        defaults.putAll(vars);
      }
      //最后把Properties设置进XMLConfigBuilder和configuration
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

读取 settings 属性

private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    //获取setting所有的 name 和 value转换为Properties
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    //获取元类 初始化reflector(缓存的类定义信息)把reflector || PUT进reflectorMap里面key是Configuration.class value是reflector
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    //迭代props里面的value 检查reflector里面有没有  没有就抛异常
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

指定 VFS 的实现

 private void loadCustomVfs(Properties props) throws ClassNotFoundException {
    //读取vfsImpl 字段
    String value = props.getProperty("vfsImpl");
    if (value != null) {
      //逗号分割字符串为数组
      String[] clazzes = value.split(",");
      //遍历 加载CLASS 设置进configuration
      for (String clazz : clazzes) {
        if (!clazz.isEmpty()) {
          @SuppressWarnings("unchecked")
          Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
          configuration.setVfsImpl(vfsImpl);
        }
      }
    }
  }

指定 MyBatis 所用日志的具体实现

  private void loadCustomLogImpl(Properties props) {
    //获取Log的子类CLASSS
    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));\
    //设置configuration的日志的具体实现
    configuration.setLogImpl(logImpl);
  }

resolveClass方法判空操作调用resolveAlias

protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }

resolveAlias调用typeAliasRegistry看是否已经有

  protected <T> Class<? extends T> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }

解析逻辑

public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      //转换为小写,
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      //判断 typeAliases容器里面存不存在
      if (typeAliases.containsKey(key)) {
        //如果有 就从容器获取
        value = (Class<T>) typeAliases.get(key);
      } else {
        //没有 通过类加载构建 Class  没有传ClassLoader  代表使用默认
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

解析 类型别名

 private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //判断是否为package
        if ("package".equals(child.getName())) {
          //获取name 属性
          String typeAliasPackage = child.getStringAttribute("name");
          //注册到configuration的typeAliasRegistry里面
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          //获取alias type属性
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            //加载CLASS
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              //注册到typeAliasRegistry  如果alias为空使用 Bean 的首字母小写的非限定类名来作为它的别名
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }
 public void registerAliases(String packageName) {
    registerAliases(packageName, Object.class);
  }

  public void registerAliases(String packageName, Class<?> superType) {
    //ResolverUtil用于查找在类路径可用并满足任意条件的类
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    //查找packageName包下所有Object的子类,也就是所有类。
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    //获取所有符合的CLASS
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for (Class<?> type : typeSet) {
      //忽略内部类和接口(包括package-info.java)
      // Ignore inner classes and interfaces (including package-info.java)
      //也跳过内部类
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        //注册
        registerAlias(type);
      }
    }
  }

objectFactory objectWrapperFactory reflectorFactory settingsElement几个方法就不解析了就是设置到configuration里面


接下来看environmentsElement

 private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        //获取default属性
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        //获取Id属性
        String id = child.getStringAttribute("id");
        //判断default属性 和 id是否一样
        if (isSpecifiedEnvironment(id)) {
          //解析事务
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //解析数据源构建工厂
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //Environment建造者模式  构建Environment
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

数据库厂商标识 databaseIdProviderElement

private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      //获取type属性
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      //兼容 VENDOR
      if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";
      }
      //获取所有的子Properties
      Properties properties = context.getChildrenAsProperties();
      //实例化
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
      //设置Properties
      databaseIdProvider.setProperties(properties);
    }
    //获取环境配置
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      //获取databaseId 并设置
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      configuration.setDatabaseId(databaseId);
    }
  }

typeHandlerElement

private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      //遍历
      for (XNode child : parent.getChildren()) {
        //如果是package 类型的
        if ("package".equals(child.getName())) {
          //获取包路径
          String typeHandlerPackage = child.getStringAttribute("name");
          //注册到typeHandlerRegistry 必须是实现了TypeHandler接口的
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          //获取java类型 jdbc类型  handler 注册到typeHandlerRegistry
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

mapperElement

 private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      //遍历子节点
      for (XNode child : parent.getChildren()) {
        //package子节点
        if ("package".equals(child.getName())) {
          //获取包路径
          String mapperPackage = child.getStringAttribute("name");
          //添加到knownMappers里面
          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());
            //解析mapper
            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.");
          }
        }
      }
    }
  }

使用的是xml主要看XMLMapperBuilder的parse方法

 //判断mapper是否解析过
    if (!configuration.isResourceLoaded(resource)) {
      // 读取整个mapper文件。
      configurationElement(parser.evalNode("/mapper"));
      // 把这个已经加载的配置文件(资源,resource)放到configuration这个Set中。
      configuration.addLoadedResource(resource);
      //绑定命名空间和保存mapper Class
      bindMapperForNamespace();
    }
    // 解析ResultMaps
    parsePendingResultMaps();
    // 解析缓存
    parsePendingCacheRefs();
    // 解析Statements
    parsePendingStatements();

configurationElement

private void configurationElement(XNode context) {
    try {
      //获取namespace属性
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        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 这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。
      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);
    }
  }

这里主要看下buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))是怎么解析的

 /**
   * 如果DatabaseId这里不为null的情况会解析两遍
   * 一遍是带DatabaseId的
   * 一边是不带
   * @param list
   */
 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) {
    //遍历解析所有的节点
    for (XNode context : list) {
      //创建一个XMLStatementBuilder去解析每个标签
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        //解析xml Statement
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

这里到了非常关键的点,statementParser.parseStatementNode()看看是怎么解析Statement的

public void parseStatementNode() {
    //获取 id 属性  
    String id = context.getStringAttribute("id");
    //获取 databaseId 属性
    String databaseId = context.getStringAttribute("databaseId");
    //做一些检查
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    //获取标签名称
    String nodeName = context.getNode().getNodeName();
    //通过标签名称构造SqlCommandType
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    //是否为查询语句
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //获取flushCache  如果flushCache为null 就isSelect取反 不然就是用flushCache的值
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    //获取useCache  如果useCache为null 就isSelect
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    //获取resultOrdered  如果resultOrdered为null 就是false
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing\
    //创建Include标签解析
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());
    //获取 parameterType 属性
    String parameterType = context.getStringAttribute("parameterType");
    //通过typeAliases 获取class如果没有就加载过来
    Class<?> parameterTypeClass = resolveClass(parameterType);
    //获取 lang属性
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> 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;
    }
    //解析xml节点,封装成SqlSource里面携带了是否为动态sql(通过SqlSource的实现类型),还有configuration和拆分成各种SqlNode的list
    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是怎么创建的和最后的mappedStatement
这里如果没有指定langDriver默认使用的XMLLanguageDriver,可以看看逻辑是怎么实现的

langDriver.createSqlSource(configuration, context, parameterTypeClass)

@Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    /**
     * 创建XML脚本构建器
     */
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    /***
     *  当sql中包含有${}时,就认为是动态SQL
     *       或者 当DML标签存在动态SQL标签名,如&lt;if&gt;,&lt;trim&gt;等,就认为是动态SQL
     *  ,如果是动态返回 {@link DynamicSqlSource} ,否则 {@link RawSqlSource}
     */
    return builder.parseScriptNode();
  }
public SqlSource parseScriptNode() {
    //解析DML标签下的子节点,封装成MixedSqlNode(里面有一个list存各种SqlNode),解析是否为动态sql
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    //判断是否为动态sql
    if (isDynamic) {
      //只创建DynamicSqlSource对象。完整可执行的sql,需要在调用getBoundSql(Parameter)方法时才能组装完成。
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      /**
       * 调用 SqlSourceBuilder类将"#{xxx}“ 替换为占位符”?",并绑定ParameterMapping,
       * 最后返回的RawSqlSource中持有一个由SqlSourceBuilder构建的SqlSource对象。
       */
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
/**
   * 解析动态SQL标签,node的子节点和文本节点进行解析,
   *  这里会检测是否属于动态就会对isDynamic设置对应的值
   */
  protected MixedSqlNode parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      /**
       * XNode.newXNode(Node node):新建一个XNode,XNode只是mybatis对Node实例的封装,用于方便操作。
       * 并不是添加node节点的功能
       */
      XNode child = node.newXNode(children.item(i));
      /**
       * Node.CDATA_SECTION_NODE:代表文档中的 CDATA 部(不会由解析器解析的文本)
       * Node.TEXT_NODE:代表元素或属性中的文本内容
       */
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return new MixedSqlNode(contents);
  }

下面就是mybatis建造者模式的案例

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 = 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 statement = statementBuilder.build();
    /**
     * 把解析完成的MappedStatement装进mappedStatements的容器了
     * key是xml上面的id
     */
    configuration.addMappedStatement(statement);
    return statement;
  }

configuration.addLoadedResource(resource)

public void addLoadedResource(String resource) {
    loadedResources.add(resource);
  }

bindMapperForNamespace()

private void bindMapperForNamespace() {
    /**
     * 通过构建助手拿到命名空间
     */
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      // 绑定类型
      Class<?> boundType = null;
      try {
        /**
         * 通过类加载器加载class
         */
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        // ignore, bound type is not required
      }
      /**
       * class不为空并且knownMappers里面没有这个key
       */
      if (boundType != null && !configuration.hasMapper(boundType)) {
        /**
         * Spring可能不知道真实的资源名称,因此我们设置了一个标志
         * 以防止再次从mapper接口加载此资源
         * 查看MapperAnnotationBuilder#loadXmlResource
         */
        /**
         * 把命名空间添加进loadedResources里面开头是namespace:
         */
        configuration.addLoadedResource("namespace:" + namespace);
        /**
         * 添加进knownMappers里面 kye是class value是MapperProxyFactory
         */
        configuration.addMapper(boundType);
      }
    }
  }

下面就是mapper的核心代码,怎么把mapper通过动态代理放到配置类的mapperRegistry里面的knownMappers里

public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }
public <T> void addMapper(Class<T> type) {
    //判断是接口
    if (type.isInterface()) {
      //查看knownMappers里面是否有
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      //是否加载完成
      boolean loadCompleted = false;
      try {
        //添加进knownMappers key是Class  value是MapperProxyFactory里面的MapperProxyFactory是当前Class
        knownMappers.put(type, new MapperProxyFactory<>(type));
         /**
         * 重要的是,必须在运行解析器之前添加类型,
         * 否则映射器解析器可能会自动尝试绑定。
         * 如果类型是已知的,则不会尝试。
         */
        //构建MapperAnnotationBuilder
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        //解析
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

parser.parse()

public void parse() {
    String resource = type.toString();
    //判断configuration里面的loadedResources里面是否有当前的resource
    if (!configuration.isResourceLoaded(resource)) {
      //解析接口同名路径下的xml
      loadXmlResource();
      //把resource设置LoadedResource
      configuration.addLoadedResource(resource);
      //设置助手的当前命名空间为接口的包名
      assistant.setCurrentNamespace(type.getName());
      //设置二级缓存(基于注解@CacheNamespace)
      parseCache();
      //设置二级缓存引用(基于注解@CacheNamespaceRef)
      parseCacheRef();
      for (Method method : type.getMethods()) {
        //判断是否为桥接方法和默认方法 是的话不处理
        if (!canHaveStatement(method)) {
          continue;
        }
        //检查构建annotationWrapper是否成功  方法上面有ResultMap注解 然后就 解析ResultMap
        if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
            && method.getAnnotation(ResultMap.class) == null) {
          parseResultMap(method);
        }
        try {
          /**
           * 解析(基于注解的方法)Statement  放入configuration
           */
          parseStatement(method);
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }
void parseStatement(Method method) {
    //获取方法的参数
    final Class<?> parameterTypeClass = getParameterType(method);
    final LanguageDriver languageDriver = getLanguageDriver(method);
    /**
     * 构造AnnotationWrapper
     * 解析方法上面的注解select.class, Update.class, Insert.class, Delete.class,
     * SelectProvider.class, UpdateProvider.class,InsertProvider.class, DeleteProvider.class
     */
    getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {
      //构建SqlSource
      final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);
      final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
      final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options)x.getAnnotation()).orElse(null);
      final String mappedStatementId = type.getName() + "." + method.getName();

      final KeyGenerator keyGenerator;
      String keyProperty = null;
      String keyColumn = null;
      //判断是否为INSERT UPDATE
      if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
        // first check for SelectKey annotation - that overrides everything else
        /**
         * 首先检查SelectKey注释-覆盖其他所有内容
         */
        SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey)x.getAnnotation()).orElse(null);
        if (selectKey != null) {
          keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
          keyProperty = selectKey.keyProperty();
        } else if (options == null) {
          keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        } else {
          keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
          keyProperty = options.keyProperty();
          keyColumn = options.keyColumn();
        }
      } else {
        keyGenerator = NoKeyGenerator.INSTANCE;
      }

      Integer fetchSize = null;
      Integer timeout = null;
      StatementType statementType = StatementType.PREPARED;
      ResultSetType resultSetType = configuration.getDefaultResultSetType();
      boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
      boolean flushCache = !isSelect;
      boolean useCache = isSelect;
      if (options != null) {
        if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
          flushCache = true;
        } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
          flushCache = false;
        }
        useCache = options.useCache();
        fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
        timeout = options.timeout() > -1 ? options.timeout() : null;
        statementType = options.statementType();
        if (options.resultSetType() != ResultSetType.DEFAULT) {
          resultSetType = options.resultSetType();
        }
      }

      String resultMapId = null;
      //如果是查询就构建resultMapId返回参数
      if (isSelect) {
        //如果有@ResultMap就获取valer通过逗号,分割
        ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
        if (resultMapAnnotation != null) {
          resultMapId = String.join(",", resultMapAnnotation.value());
        } else {
          /**
           * 解析@Results通过接口的全路径名加上.加上@Results的id值
           * 没有@Results注解就遍历方法的返回参数通过 -加上参数名
           * 如果没有返回参数名就用-void
           */
          resultMapId = generateResultMapName(method);
        }
      }

      assistant.addMappedStatement(
          mappedStatementId,
          sqlSource,
          statementType,
          sqlCommandType,
          fetchSize,
          timeout,
          // ParameterMapID
          null,
          parameterTypeClass,
          resultMapId,
          getReturnType(method),
          resultSetType,
          flushCache,
          useCache,
          // TODO gcode issue #577
          false,
          keyGenerator,
          keyProperty,
          keyColumn,
          statementAnnotation.getDatabaseId(),
          languageDriver,
          // ResultSets
          options != null ? nullOrEmpty(options.resultSets()) : null);
    });
  }

上面就是通过注解解为MappedStatement的流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值