前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mybatis初始化的builder建造者模式

Mybatis初始化的builder建造者模式

作者头像
算法之名
发布2019-08-20 10:21:34
2K0
发布2019-08-20 10:21:34
举报
文章被收录于专栏:算法之名算法之名

建造者模式有四大角色

  • 建造者接口:用于定义建造者构建产品对象的各部分的行为
  • 导演:调用建造者接口或抽象类来进行建造流程的打造。
  • 具体建造者:实现建造者接口或抽象类的两类方法:一是建造方法,一是获取构建好的产品
  • 产品:产品对象就是用户需要使用的复杂对象

具体的实例可以参考 设计模式整理

在Mybatis的初始化的主要工作是加载并解析mybatis-config.xml的配置文件、映射配置文件以及相关的注解信息。因为使用了建造者模式,BashBuilder抽象类即为建造者接口的角色。它的核心字段内容如下

代码语言:javascript
复制
//Mybatis初始化过程的核心对象,Mybatis中几乎全部的配置信息会保存到该对象中。该对象在Mybatis初始化过程中创建且是全局唯一的
protected final Configuration configuration;
//定义的别名都会记录在该对象中
protected final TypeAliasRegistry typeAliasRegistry;
//指定数据库类型与Java类型的转换器
protected final TypeHandlerRegistry typeHandlerRegistry;

通用方法

代码语言:javascript
复制
protected Class<?> resolveClass(String alias) {
  if (alias == null) {
    return null;
  }
  try {
    return resolveAlias(alias);
  } catch (Exception e) {
    throw new BuilderException("Error resolving class. Cause: " + e, e);
  }
}
代码语言:javascript
复制
protected Class<?> resolveAlias(String alias) {
  return typeAliasRegistry.resolveAlias(alias);
}
代码语言:javascript
复制
protected ResultSetType resolveResultSetType(String alias) {
  if (alias == null) {
    return null;
  }
  try {
    return ResultSetType.valueOf(alias);
  } catch (IllegalArgumentException e) {
    throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
  }
}
代码语言:javascript
复制
protected ParameterMode resolveParameterMode(String alias) {
  if (alias == null) {
    return null;
  }
  try {
    return ParameterMode.valueOf(alias);
  } catch (IllegalArgumentException e) {
    throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
  }
}
代码语言:javascript
复制
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
  if (typeHandlerAlias == null) {
    return null;
  }
  //通过类型别名映射解析别名
  Class<?> type = resolveClass(typeHandlerAlias);
  //如果type不为null且type不为TypeHandler接口的实现类,抛出异常
  if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
    throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
  }
  //将type强制转换成TypeHandler的实现类
  @SuppressWarnings( "unchecked" ) // already verified it is a TypeHandler
  Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
  return resolveTypeHandler(javaType, typeHandlerType);
}
代码语言:javascript
复制
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
  if (typeHandlerType == null) {
    return null;
  }
  //从类型处理器注册器中获取typeHandlerType类实例对应的TypeHandler对象
  TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
  if (handler == null) {
    //如果handler对象为null,从类型处理器注册器中获取以javaType为构造参数来构造的typeHandlerType的实例对象
    handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
  }
  return handler;
}

它要构建的方法非常多,但跟我们设计模式整理中试例不同的是,它并不针对一种产品进行构建,所以它并没有一个抽象方法。这就需要我们针对它不同的子类来分开看它的构建方法。

XMLConfigBuilder是BaseBuilder众多子类之一,负责解析mybatis-config.xml配置文件,核心字段如下

代码语言:javascript
复制
private boolean parsed; //标识是否已经解析过mybatis-config.xml配置文件
private final XPathParser parser; //用于解析mybatis-config.xml配置文件的XPathParser对象
private String environment; //标识<enviroment>配置的名称,默认读取<enviroment>标签的default属性
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); //反射工厂,用于创建和缓存反射对象

解析mybatis-config.xml配置文件的入口为parse()方法

代码语言:javascript
复制
public Configuration parse() {
  if (parsed) { //判断是否已经完成对mybatis-config.xml配置文件的解析
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //在mybatis-config.xml配置文件中查找<configuration>节点,并开始解析
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

比如有这样一份mybatis-config.xml文件

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties>
       <property name="dialect" value="mysql" />
    </properties>
    <!-- 引入database.properties文件 -->
    <properties resource="database.properties"/>  
    <!-- 为model下面的包设置别名 -->
    <typeAliases>
       <package name="com.xxx.model"/>
       <package name="com.xxx.vo"/>
    </typeAliases>
    <!-- 设置运行参数 -->
    <settings>
        <!-- 全局映射器启用缓存 -->
        <setting name="cacheEnabled" value="false" />
        <!-- 查询时,关闭关联对象及时加载以提高性能 -->
        <setting name="lazyLoadingEnabled" value="false" />
        <!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指定),不会加载关联表的所有字段,以提高性能 -->
        <setting name="aggressiveLazyLoading" value="false" />
        <!-- 对于位置的SQL查询,允许返回不同的结果集以达到通用的效果 -->
        <setting name="multipleResultSetsEnabled" value="true" />
        <!-- 允许使用列标签代替列明 -->
        <setting name="useColumnLabel" value="true" />
        <!-- 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值), 数据表的pk生成策略将被覆盖 -->
        <setting name="useGeneratedKeys" value="false" />
        <!-- 给予被嵌套的resultMap以字段-属性的映射支持 -->
        <setting name="autoMappingBehavior" value="PARTIAL" />
        <!-- 对于批量更新操作缓存SQL以提高性能 -->
        <setting name="defaultExecutorType" value="REUSE" />
        <!-- 数据库超过25000秒仍未响应则超时 -->
        <setting name="defaultStatementTimeout" value="25000" />
        <!-- 打印查询语句 -->
         <!--<setting name="logImpl" value="STDOUT_LOGGING" />-->
    </settings>
</configuration>
代码语言:javascript
复制
private void parseConfiguration(XNode root) {
  try {
    //解析<properties>节点
    propertiesElement(root.evalNode("properties"));
    //解析<settings>节点
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    //设置vfsImpl字段
    loadCustomVfs(settings);
    //解析<typeAliases>节点
    typeAliasesElement(root.evalNode("typeAliases"));
    //解析<plugins>节点
    pluginElement(root.evalNode("plugins"));
    //解析<objectFactory>节点
    objectFactoryElement(root.evalNode("objectFactory"));
    //解析<objectWrapperFactory>节点
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    //解析<reflectorFactory>节点
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    //将settings值设置到全局配置中,基本都是对枚举的操作
    settingsElement(settings);
    //解析<environments>节点
    environmentsElement(root.evalNode("environments"));
    //解析<databaseIdProvider>节点
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    //解析<typeHandlers>节点
    typeHandlerElement(root.evalNode("typeHandlers"));
    //解析<mappers>节点
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}
代码语言:javascript
复制
private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    //解析<properties>的子节点<property>的name和value属性,并记录到Properties中
    Properties defaults = context.getChildrenAsProperties();
    //解析<properties>的resource和url属性,这两个属性用于确定properties配置文件的位置
    String resource = context.getStringAttribute("resource");
    String url = context.getStringAttribute("url");
    //如果resource属性和url属性不能同时存在,否则会抛出异常
    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对象中的variables集合合并
    Properties vars = configuration.getVariables();
    if (vars != null) {
      defaults.putAll(vars);
    }
    //更新XPathParser和Configuration的variables字段
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}
代码语言:javascript
复制
private Properties settingsAsProperties(XNode context) {
  if (context == null) {
    return new Properties();
  }
  //解析<Settings>的子节点<Setting>的name和value属性,并返回Properties对象
  Properties props = context.getChildrenAsProperties();
  //创建Configuration对应的MetaClass对象,MetaClass之前有说过是判断类实例是否有getter,setter属性的对象
  MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  //遍历setting标签下的name属性,并判断该属性是否有setter属性,没有则抛出异常
  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;
}
代码语言:javascript
复制
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
  //获取<Setting>里name为vfsImpl的值
  String value = props.getProperty("vfsImpl");
  if (value != null) {
    String[] clazzes = value.split(",");
    for (String clazz : clazzes) {
      if (!clazz.isEmpty()) {
        @SuppressWarnings("unchecked")
        Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
        configuration.setVfsImpl(vfsImpl);
      }
    }
  }
}
代码语言:javascript
复制
private void typeAliasesElement(XNode parent) {
  if (parent != null) {
    for (XNode child : parent.getChildren()) { //处理全部子节点
      if ("package".equals(child.getName())) { //处理package节点
        //获取指定的包名
        String typeAliasPackage = child.getStringAttribute("name");
        //通过TypeAliasRegistry扫描制定包中所有的类,并解析@Alias注解,完成别名注册,后面有详细的分析代码
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } else { //处理<typeAlias>节点
        //获取指定的别名
        String alias = child.getStringAttribute("alias");
        //获取别名对应的名称
        String type = child.getStringAttribute("type");
        try {
          //根据别名对应的名称进行类装载,生成对应的Class实例
          Class<?> clazz = Resources.classForName(type);
          if (alias == null) {
            //如果别名为null,扫描@Alias注解,完成注册
            typeAliasRegistry.registerAlias(clazz);
          } else {
            //如果别名不为null,注册别名
            typeAliasRegistry.registerAlias(alias, clazz);
          }
        } catch (ClassNotFoundException e) {
          throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
        }
      }
    }
  }
}
代码语言:javascript
复制
private void pluginElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) { //遍历所有子节点(即<plugin>节点)
      //获取<plugin>节点的interceptor属性的值
      String interceptor = child.getStringAttribute("interceptor");
      //获取<plugin>节点下<properties>配置的信息,并形成Properties对象
      Properties properties = child.getChildrenAsProperties();
      //通过类型别名映射解析别名,并实例化为一个拦截器对象
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
      //设置拦截器对象的属性
      interceptorInstance.setProperties(properties);
      //在全局配置中添加拦截器对象
      configuration.addInterceptor(interceptorInstance);
    }
  }
}
代码语言:javascript
复制
private void objectFactoryElement(XNode context) throws Exception {
  if (context != null) {
    //获取<objectFactory>节点的type属性
    String type = context.getStringAttribute("type");
    //获取<objectFactory>节点下配置的信息,并形成Properties对象
    Properties properties = context.getChildrenAsProperties();
    //通过类型别名映射解析别名,并实例化为一个对象工厂对象
    ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
    //设置对象工厂的属性
    factory.setProperties(properties);
    //在全局配置器中添加对象工厂对象
    configuration.setObjectFactory(factory);
  }
}
代码语言:javascript
复制
private void objectWrapperFactoryElement(XNode context) throws Exception {
  if (context != null) {
    //获取<objectWrapperFactory>节点的type属性
    String type = context.getStringAttribute("type");
    //通过类型别名映射解析别名,并实例化为一个对象包装工厂对象
    ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
    //在全局配置器中添加对象包装工厂对象
    configuration.setObjectWrapperFactory(factory);
  }
}
代码语言:javascript
复制
private void reflectorFactoryElement(XNode context) throws Exception {
  if (context != null) {
     //获取<reflectorFactory>节点的type属性
     String type = context.getStringAttribute("type");
     //通过类型别名映射解析别名,并实例化为一个反射工厂对象
     ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
     //在全局配置器中添加反射工厂对象
     configuration.setReflectorFactory(factory);
  }
}
代码语言:javascript
复制
private void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    //未设置enviroment字段,则获取<environments>节点的default属性
    if (environment == null) {
      environment = context.getStringAttribute("default");
    }
    //遍历子节点(<environment>节点)
    for (XNode child : context.getChildren()) {
      //获取<environment>节点id属性
      String id = child.getStringAttribute("id");
      //如果设置的enviroment字段与id相同,有一项为null则抛出异常
      if (isSpecifiedEnvironment(id)) {
        //解析<transactionManager>节点
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        //解析<dataSource>节点
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        //通过数据源工厂获取数据源
        DataSource dataSource = dsFactory.getDataSource();
        //创建Environment,此处使用Build方式
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        //在全局配置器中添加环境对象(其中封装了事务工厂以及数据源)
        configuration.setEnvironment(environmentBuilder.build());
      }
    }
  }
}

关于build方式,请参考 用Build来构建对象的写法

代码语言:javascript
复制
private boolean isSpecifiedEnvironment(String id) {
  if (environment == null) {
    throw new BuilderException("No environment specified.");
  } else if (id == null) {
    throw new BuilderException("Environment requires an id attribute.");
  } else if (environment.equals(id)) {
    return true;
  }
  return false;
}
代码语言:javascript
复制
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
  if (context != null) {
    //获取<transactionManager>节点的type属性
    String type = context.getStringAttribute("type");
    //获取<transactionManager>节点下配置的信息,并形成Properties对象
    Properties props = context.getChildrenAsProperties();
    //通过类型别名映射解析别名,并实例化为一个事务工厂对象
    TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
    //设置事务工厂的属性
    factory.setProperties(props);
    return factory;
  }
  throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
代码语言:javascript
复制
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  if (context != null) {
    //获取<dataSource>节点的type属性
    String type = context.getStringAttribute("type");
    //获取<dataSource>节点下配置的信息,并形成Properties对象
    Properties props = context.getChildrenAsProperties();
    //通过类型别名映射解析别名,并实例化为一个数据源工厂对象
    DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
    //设置数据源工厂的属性
    factory.setProperties(props);
    return factory;
  }
  throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
代码语言:javascript
复制
private void databaseIdProviderElement(XNode context) throws Exception {
  DatabaseIdProvider databaseIdProvider = null;
  if (context != null) {
    //获取<databaseIdProvider>节点的type属性
    String type = context.getStringAttribute("type");
    //为了保持兼容性,修改type取值
    if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";
    }
    //获取<databaseIdProvider>节点下配置的信息,并形成Properties对象
    Properties properties = context.getChildrenAsProperties();
    //通过类型别名映射解析别名,并实例化为一个数据库类型提供者(对哪些数据库进行支持)对象
    databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
    //设置数据库类型提供者的属性
    databaseIdProvider.setProperties(properties);
  }
  //从全局配置器中取出带数据源的环境对象
  Environment environment = configuration.getEnvironment();
  //如果环境对象以及数据库类型提供者对象都不为null
  if (environment != null && databaseIdProvider != null) {
    //从数据库类型提供者的属性中获取数据库id(数据库名称key对应的value),如果属性中没有直接获取数据库名称
    String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
    //在全局配置器中添加数据库Id字符串
    configuration.setDatabaseId(databaseId);
  }
}
代码语言:javascript
复制
private void typeHandlerElement(XNode parent) throws Exception {
  if (parent != null) {
    //如果<typeHandler>节点不为null,遍历其所有子节点
    for (XNode child : parent.getChildren()) {
      //如果子节点的名称为package
      if ("package".equals(child.getName())) {
        //获取其子节点的name属性
        String typeHandlerPackage = child.getStringAttribute("name");
        //将该name属性为类名的类与数据库字段以及类型处理器对象添加到集合中
        typeHandlerRegistry.register(typeHandlerPackage);
      } else { //如果其子节点的名称不为package
        //获取子节点的javaType属性
        String javaTypeName = child.getStringAttribute("javaType");
        //获取子节点的jdbcType属性
        String jdbcTypeName = child.getStringAttribute("jdbcType");
        //获取子节点的handler属性
        String handlerTypeName = child.getStringAttribute("handler");
        //通过类型别名映射解析别名,获取java类型的类实例
        Class<?> javaTypeClass = resolveClass(javaTypeName);
        //根据jdbcType属性获取一个jdbcType的枚举
        JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
        //通过类型别名映射解析别名,获取类型处理器的类实例
        Class<?> typeHandlerClass = resolveClass(handlerTypeName);
        //如果java类型的类实例不为null
        if (javaTypeClass != null) {
          //如果jdbcType的枚举为null
          if (jdbcType == null) {
            //将java类型,空的数据库字段类型,类型处理器对象添加到集合中
            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
          } else {
            //否则将java类型,数据库字段类型,类型处理器对象添加到集合中
            typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
          }
        } else {
          //如果java类型的类实例为null,将空的java类型,数据库字段类型,类型处理器对象添加到集合中
          typeHandlerRegistry.register(typeHandlerClass);
        }
      }
    }
  }
}
代码语言:javascript
复制
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    //如果<mappers>节点不为null,遍历该节点的子节点
    for (XNode child : parent.getChildren()) {
      //如果子节点名称为package
      if ("package".equals(child.getName())) {
        //获取该子节点的name属性
        String mapperPackage = child.getStringAttribute("name");
        //在全局配置器中增加Mappers属性为该子节点的name属性
        configuration.addMappers(mapperPackage);
      } else { //如果子节点的名称不为package
        //获取子节点的resource属性
        String resource = child.getStringAttribute("resource");
        //获取子节点的url属性
        String url = child.getStringAttribute("url");
        //获取子节点的class属性
        String mapperClass = child.getStringAttribute("class");
        //由于这三种属性互斥,只能出现一种属性,当为resource属性时
        if (resource != null && url == null && mapperClass == null) {
          //将resource属性放入错误上下文的resource属性中
          ErrorContext.instance().resource(resource);
          //打开resource的URL连接,获取数据流
          InputStream inputStream = Resources.getResourceAsStream(resource);
          //创建XMLMapperBuilder对象,解析映射配置文件
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          mapperParser.parse();
          //当为url属性时
        } else if (resource == null && url != null && mapperClass == null) {
          //将url属性放入错误上下文的resource属性中
          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) {
          //如果<mapper>节点指定了class属性,则向全局配置器中注册该接口
          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.");
        }
      }
    }
  }
}

在TypeAliasRegistry下

代码语言:javascript
复制
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>(); //类型别名,在构造函数中将各种基本类型放入了HashMap中
代码语言:javascript
复制
public void registerAliases(String packageName){
  registerAliases(packageName, Object.class);
}
代码语言:javascript
复制
public void registerAliases(String packageName, Class<?> superType){
  //创建一个工具类对象
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  //查找包名下的所有资源,如果为.class,则添加到该工具对象的匹配集合中
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  //获取该工具对象的匹配集合
  Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
  //遍历该集合
  for(Class<?> type : typeSet){
    // Ignore inner classes and interfaces (including package-info.java)
    // Skip also inner classes. See issue #6
    //如果该集合项不是匿名类,接口,成员类(类内部的类)
    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
      //将该集合项添加到别名映射中,如果有@Alias注解,则以该注解的value作为别名,如果没有则以类名(不包含包名)作为别名,别名为key,类实例为value
      registerAlias(type);
    }
  }
}
代码语言:javascript
复制
public void registerAlias(Class<?> type) {
  //获得类的名称(不包含包名),做为别名
  String alias = type.getSimpleName();
  //获得该类的@Alias注解
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    //如果该注解不为null,获取该注解的配置value做为别名
    alias = aliasAnnotation.value();
  } 
  //将别名与类实例添加为map映射
  registerAlias(alias, type);
}
代码语言:javascript
复制
public void registerAlias(String alias, Class<?> value) {
  if (alias == null) {
    throw new TypeException("The parameter alias cannot be null");
  }
  // issue #748
  //将别名英文大写字符转成小写
  String key = alias.toLowerCase(Locale.ENGLISH);
  //如果类型别名Map中已经有了该别名的键,且值不为null,又不为value,抛出异常
  if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
    throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
  }
  //将key,value加入到类型别名映射中
  TYPE_ALIASES.put(key, value);
}
代码语言:javascript
复制
public <T> Class<T> resolveAlias(String string) {
  try {
    if (string == null) {
      return null;
    }
    // issue #748
    //将别名英文大写字符转成小写
    String key = string.toLowerCase(Locale.ENGLISH);
    Class<T> value;
    //类型别名映射中是否包含该别名,包含则取出该别名的Class实例
    if (TYPE_ALIASES.containsKey(key)) {
      value = (Class<T>) TYPE_ALIASES.get(key);
    } else {
      //不包含则通过反射获取一个类实例
      value = (Class<T>) Resources.classForName(string);
    }
    return value;
  } catch (ClassNotFoundException e) {
    throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  }
}

在ResolverUtil下

代码语言:javascript
复制
//匹配项集合
private Set<Class<? extends T>> matches = new HashSet<Class<? extends T>>();
代码语言:javascript
复制
public ResolverUtil<T> find(Test test, String packageName) {
  //把包名的.号替换成/,获取路径
  String path = getPackagePath(packageName);

  try {
    //以单例模式获取一个VFS子类的实例,并用该实例将path下的所有资源(包括所有文件夹及子文件夹)放入列表中
    List<String> children = VFS.getInstance().list(path);
    //遍历该列表
    for (String child : children) {
      //如果该列表项以.class结尾
      if (child.endsWith(".class")) {
        //如果该列表项为test的子类,将其添加到匹配项matches集合中
        addIfMatching(test, child);
      }
    }
  } catch (IOException ioe) {
    log.error("Could not read package: " + packageName, ioe);
  }

  return this;
}
代码语言:javascript
复制
protected void addIfMatching(Test test, String fqn) {
  try {
    //获取类的名称(包含包名)
    String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
    //得到类装载器
    ClassLoader loader = getClassLoader();
    if (log.isDebugEnabled()) {
      log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
    }
    //装载该类,得到该类的类实例
    Class<?> type = loader.loadClass(externalName);
    //如果该类实例不为null
    if (test.matches(type)) {
      //将其添加到匹配项集合中
      matches.add((Class<T>) type);
    }
  } catch (Throwable t) {
    log.warn("Could not examine class '" + fqn + "'" + " due to a " +
        t.getClass().getName() + " with message: " + t.getMessage());
  }
}
代码语言:javascript
复制
@Override
public boolean matches(Class<?> type) {
  //返回type实例是否为null,由于调用处parent为Object.class,必然为其父类
  return type != null && parent.isAssignableFrom(type);
}
代码语言:javascript
复制
public Set<Class<? extends T>> getClasses() {
  return matches;
}

在VFS中,VFS表示虚拟文件系统,是一个抽象类,所以要获取它的实例只能获取一个它的子类的实例

代码语言:javascript
复制
//由addimplclass(class)方法添加实现的列表,由用户扩展使用
public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<Class<? extends VFS>>();
代码语言:javascript
复制
//内置的子类
public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };
代码语言:javascript
复制
public static VFS getInstance() {
  //返回VFS容器的实例,这是一个单例模式的容器
  return VFSHolder.INSTANCE;
}
代码语言:javascript
复制
private static class VFSHolder {
  static final VFS INSTANCE = createVFS();

  @SuppressWarnings("unchecked")
  static VFS createVFS() {
    //创建一个VFS子类的列表
    List<Class<? extends VFS>> impls = new ArrayList<Class<? extends VFS>>();
    //将所有可能的子类全部添加到列表中
    impls.addAll(USER_IMPLEMENTATIONS);
    impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

    //遍历所有的子类,直到有一个能实例化成对象的
    VFS vfs = null;
    for (int i = 0; vfs == null || !vfs.isValid(); i++) {
      Class<? extends VFS> impl = impls.get(i);
      try {
        vfs = impl.newInstance();
        if (vfs == null || !vfs.isValid()) {
          if (log.isDebugEnabled()) {
            log.debug("VFS implementation " + impl.getName() +
                " is not valid in this environment.");
          }
        }
      } catch (InstantiationException e) {
        log.error("Failed to instantiate " + impl, e);
        return null;
      } catch (IllegalAccessException e) {
        log.error("Failed to instantiate " + impl, e);
        return null;
      }
    }

    if (log.isDebugEnabled()) {
      log.debug("Using VFS adapter " + vfs.getClass().getName());
    }

    return vfs;
  }
}
代码语言:javascript
复制
public List<String> list(String path) throws IOException {
  List<String> names = new ArrayList<String>();
  for (URL url : getResources(path)) {
    names.addAll(list(url, path));
  }
  return names;
}
代码语言:javascript
复制
protected static List<URL> getResources(String path) throws IOException {
  //通过JVM装载器装载path下的资源
  return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
}

在DefaultVFS中

代码语言:javascript
复制
private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 }; //jar文件的魔数
代码语言:javascript
复制
@Override
public List<String> list(URL url, String path) throws IOException {
  InputStream is = null;
  try {
    List<String> resources = new ArrayList<String>();

    //查找指定地址的jar包的url
    URL jarUrl = findJarForResource(url);
    if (jarUrl != null) {
      //如果该url不为null,打开连接,得到传输流
      is = jarUrl.openStream();
      if (log.isDebugEnabled()) {
        log.debug("Listing " + url);
      }
      //拿到jar包里面所有的文件列表
      resources = listResources(new JarInputStream(is), path);
    }
    else { //如果该url为null
      //创建一个子List
      List<String> children = new ArrayList<String>();
      try {
        //如果该url本身就是一个jar文件地址
        if (isJar(url)) {
          // Some versions of JBoss VFS might give a JAR stream even if the resource
          // referenced by the URL isn't actually a JAR
          //url打开连接,建立数据流
          is = url.openStream();
          //以该数据流建立一个jar包的数据流
          JarInputStream jarInput = new JarInputStream(is);
          if (log.isDebugEnabled()) {
            log.debug("Listing " + url);
          }
          //遍历jar包中的所有资源
          for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;) {
            if (log.isDebugEnabled()) {
              log.debug("Jar entry: " + entry.getName());
            }
            //将所有的资源名称添加到子List中
            children.add(entry.getName());
          }
          jarInput.close();
        }
        else { //如果本身不为一个jar文件的url
          /*
           * Some servlet containers allow reading from directory resources like a
           * text file, listing the child resources one per line. However, there is no
           * way to differentiate between directory and file resources just by reading
           * them. To work around that, as each line is read, try to look it up via
           * the class loader as a child of the current resource. If any line fails
           * then we assume the current resource is not a directory.
           */
          //打开连接创建一个数据流
          is = url.openStream();
          //创建一个缓存读取器
          BufferedReader reader = new BufferedReader(new InputStreamReader(is));
          //创建一个多行列表
          List<String> lines = new ArrayList<String>();
          //遍历读取器读取出来的每一行
          for (String line; (line = reader.readLine()) != null;) {
            if (log.isDebugEnabled()) {
              log.debug("Reader entry: " + line);
            }
            //将每一行添加到行列表中
            lines.add(line);
            //如果JVM装载器装载的该行资源为空,清空行列表,退出遍历
            if (getResources(path + "/" + line).isEmpty()) {
              lines.clear();
              break;
            }
          }
          //如果该行列表不为空
          if (!lines.isEmpty()) {
            if (log.isDebugEnabled()) {
              log.debug("Listing " + url);
            }
            //将行列表添加到子列表中
            children.addAll(lines);
          }
        }
      } catch (FileNotFoundException e) {
        /*
         * For file URLs the openStream() call might fail, depending on the servlet
         * container, because directories can't be opened for reading. If that happens,
         * then list the directory directly instead.
         */
        //如果该url的协议为文件协议
        if ("file".equals(url.getProtocol())) {
          //创建该文件对象
          File file = new File(url.getFile());
          if (log.isDebugEnabled()) {
              log.debug("Listing directory " + file.getAbsolutePath());
          }
          //如果该对象为一个文件夹
          if (file.isDirectory()) {
            if (log.isDebugEnabled()) {
                log.debug("Listing " + url);
            }
            //将该文件夹的文件数组转成列表并赋给子列表
            children = Arrays.asList(file.list());
          }
        }
        else {
          // No idea where the exception came from so rethrow it
          throw e;
        }
      }

      //将该url转成字符串
      String prefix = url.toExternalForm();
      //如果该字符串不以/结尾,添加/结尾
      if (!prefix.endsWith("/")) {
        prefix = prefix + "/";
      }

      //遍历子列表
      for (String child : children) {
        //拼接path与子列表项
        String resourcePath = path + "/" + child;
        //将拼接后的路径添加到资源列表中
        resources.add(resourcePath);
        //对拼接后的子路径生成新的url
        URL childUrl = new URL(prefix + child);
        //递归调用,查找子路径的全部资源放入资源列表中
        resources.addAll(list(childUrl, resourcePath));
      }
    }

    return resources;
  } finally {
    if (is != null) {
      try {
        is.close();
      } catch (Exception e) {
        // Ignore
      }
    }
  }
}
代码语言:javascript
复制
protected URL findJarForResource(URL url) throws MalformedURLException {
  if (log.isDebugEnabled()) {
    log.debug("Find JAR URL: " + url);
  }

  // If the file part of the URL is itself a URL, then that URL probably points to the JAR
  try {
    for (;;) {
      //不断循环查找一个能够以文件构建的url实例
      url = new URL(url.getFile());
      if (log.isDebugEnabled()) {
        log.debug("Inner URL: " + url);
      }
    }
  } catch (MalformedURLException e) {
    // This will happen at some point and serves as a break in the loop
  }

  //将找到的url以字符串形式转换为StringBuilder
  StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
  //检查该StringBuilder是否以.jar结尾
  int index = jarUrl.lastIndexOf(".jar");
  if (index >= 0) {
    jarUrl.setLength(index + 4);
    if (log.isDebugEnabled()) {
      log.debug("Extracted JAR URL: " + jarUrl);
    }
  }
  else {
    if (log.isDebugEnabled()) {
      log.debug("Not a JAR: " + jarUrl);
    }
    return null;
  }

  // Try to open and test it
  try {
    //以该.jar结尾的字符串构建一个新的url实例
    URL testUrl = new URL(jarUrl.toString());
    //根据魔数判断是否是jar文件,返回该url
    if (isJar(testUrl)) {
      return testUrl;
    }
    else { //如果魔数判断不为jar文件,却以jar结尾
      // WebLogic fix: check if the URL's file exists in the filesystem.
      if (log.isDebugEnabled()) {
        log.debug("Not a JAR: " + jarUrl);
      }
      //将testUrl的文件路径字符串替换StringBuilder的所有字符串
      jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
      //创建一个该文件路径的文件对象
      File file = new File(jarUrl.toString());

      //如果该文件不存在,生成一个新文件
      if (!file.exists()) {
        try {
          file = new File(URLEncoder.encode(jarUrl.toString(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
          throw new RuntimeException("Unsupported encoding?  UTF-8?  That's unpossible.");
        }
      }
      
      if (file.exists()) {
        if (log.isDebugEnabled()) {
          log.debug("Trying real file: " + file.getAbsolutePath());
        }
        testUrl = file.toURI().toURL();
        if (isJar(testUrl)) {
          return testUrl;
        }
      }
    }
  } catch (MalformedURLException e) {
    log.warn("Invalid JAR URL: " + jarUrl);
  }

  if (log.isDebugEnabled()) {
    log.debug("Not a JAR: " + jarUrl);
  }
  return null;
}
代码语言:javascript
复制
protected boolean isJar(URL url) {
  return isJar(url, new byte[JAR_MAGIC.length]);
}
代码语言:javascript
复制
protected boolean isJar(URL url, byte[] buffer) {
  InputStream is = null;
  try {
    //打开url的连接,得到传输数据流
    is = url.openStream();
    //将前4位读取到字节数组中
    is.read(buffer, 0, JAR_MAGIC.length);
    //如果前4位与jar文件类型的魔数相同,返回true
    if (Arrays.equals(buffer, JAR_MAGIC)) {
      if (log.isDebugEnabled()) {
        log.debug("Found JAR: " + url);
      }
      return true;
    }
  } catch (Exception e) {
    // Failure to read the stream means this is not a JAR
  } finally {
    if (is != null) {
      try {
        is.close();
      } catch (Exception e) {
        // Ignore
      }
    }
  }

  return false;
}
代码语言:javascript
复制
protected List<String> listResources(JarInputStream jar, String path) throws IOException {
  //如果path不以/开头,则增加/开头
  if (!path.startsWith("/")) {
    path = "/" + path;
  }
  //如果path不以/结尾,则增加/结尾
  if (!path.endsWith("/")) {
    path = path + "/";
  }

  // Iterate over the entries and collect those that begin with the requested path
  List<String> resources = new ArrayList<String>();
  //遍历jar包里面的所有资源
  for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
    //如果该资源不是一个文件夹
    if (!entry.isDirectory()) {
      //获取该资源的名称
      String name = entry.getName();
      //如果该名称不以/开头,则添加/开头
      if (!name.startsWith("/")) {
        name = "/" + name;
      }

      //如果该名称以path开头
      if (name.startsWith(path)) {
        if (log.isDebugEnabled()) {
          log.debug("Found resource: " + name);
        }
        //去除开头/,并添加到List中
        resources.add(name.substring(1));
      }
    }
  }
  return resources;
}

在Resources中

代码语言:javascript
复制
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper(); //类加载器包装器
代码语言:javascript
复制
public static Class<?> classForName(String className) throws ClassNotFoundException {
  return classLoaderWrapper.classForName(className);
}
代码语言:javascript
复制
public static InputStream getResourceAsStream(String resource) throws IOException {
  return getResourceAsStream(null, resource);
}
代码语言:javascript
复制
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
  if (in == null) {
    throw new IOException("Could not find resource " + resource);
  }
  return in;
}

在ClassLoaderWrapper中

代码语言:javascript
复制
public Class<?> classForName(String name) throws ClassNotFoundException {
  return classForName(name, getClassLoaders(null));
}
代码语言:javascript
复制
Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
  //遍历类加载器数组
  for (ClassLoader cl : classLoader) {

    if (null != cl) {

      try {
        //如果该类加载器不为null,使用该类加载器加载类实例,并初始化
        Class<?> c = Class.forName(name, true, cl);

        if (null != c) {
          //如果该类实例不为null,返回该类实例
          return c;
        }

      } catch (ClassNotFoundException e) {
        // we'll ignore this until all classloaders fail to locate the class
      }

    }

  }

  throw new ClassNotFoundException("Cannot find class: " + name);

}
代码语言:javascript
复制
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
  //返回一个类加载器数组,包括各种可能的类加载器
  return new ClassLoader[]{
      classLoader,
      defaultClassLoader,
      Thread.currentThread().getContextClassLoader(),
      getClass().getClassLoader(),
      systemClassLoader};
}
代码语言:javascript
复制
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
  return getResourceAsStream(resource, getClassLoaders(classLoader));
}
代码语言:javascript
复制
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  //遍历类加载器数组
  for (ClassLoader cl : classLoader) {
    if (null != cl) {

      // try to find the resource as passed
      //根据resource字符串获取一个URL,通过该URL打开连接,产生一个数据流
      InputStream returnValue = cl.getResourceAsStream(resource);

      // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
      //如果未打开URL连接,给resource加上/前缀,重新打开连接,生成数据流
      if (null == returnValue) {
        returnValue = cl.getResourceAsStream("/" + resource);
      }

      if (null != returnValue) {
        return returnValue;
      }
    }
  }
  return null;
}

在VendorDatabaseIdProvider中

代码语言:javascript
复制
@Override
public String getDatabaseId(DataSource dataSource) {
  if (dataSource == null) {
    throw new NullPointerException("dataSource cannot be null");
  }
  try {
    return getDatabaseName(dataSource);
  } catch (Exception e) {
    log.error("Could not get a databaseId from dataSource", e);
  }
  return null;
}
代码语言:javascript
复制
private String getDatabaseName(DataSource dataSource) throws SQLException {
  //获取数据库的产品名称
  String productName = getDatabaseProductName(dataSource);
  //如果设置的属性对象不为null
  if (this.properties != null) {
    //遍历该属性对象
    for (Map.Entry<Object, Object> property : properties.entrySet()) {
      //如果数据库的产品名称中包含了属性对象的key
      if (productName.contains((String) property.getKey())) {
        //返回属性对象的value
        return (String) property.getValue();
      }
    }
    //没有匹配到,返回null
    return null;
  }
  //如果设置的属性对象为null,返回数据库的产品名称
  return productName;
}
代码语言:javascript
复制
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
  Connection con = null;
  try {
    //通过数据源获取连接
    con = dataSource.getConnection();
    //通过连接获取数据库元数据
    DatabaseMetaData metaData = con.getMetaData();
    //返回数据库的产品名称
    return metaData.getDatabaseProductName();
  } finally {
    if (con != null) {
      try {
        con.close();
      } catch (SQLException e) {
        // ignored
      }
    }
  }
}

在TypeHandlerRegistry中,TypeHandlerRegistry是一个类型处理器注册器,在构造函数中注册了一系列的java类型和数据库字段类型的映射关系

代码语言:javascript
复制
//Java类型与数据库字段的映射,value为数据库字段与其类型处理器之间的映射
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
代码语言:javascript
复制
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); //空映射
代码语言:javascript
复制
//所有类型与类型处理器的映射
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
代码语言:javascript
复制
public void register(String packageName) {  
  //创建一个工具类对象
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  //查找包名下的所有资源,如果为TypeHandler.class,则添加到该工具对象的匹配集合中
  resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
  //获取该工具对象的匹配集合
  Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
  //遍历该集合
  for (Class<?> type : handlerSet) {
    //Ignore inner classes and interfaces (including package-info.java) and abstract classes
    //如果该集合项不是匿名类,接口,抽象类
    if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
      //根据实际情况(包含集合项为null),将集合项与数据库字段类型(包括数据库字段类型为null)与类型处理器对象注册到集合中
      register(type);
    }
  }
}
代码语言:javascript
复制
public void register(Class<?> typeHandlerClass) {
  boolean mappedTypeFound = false;
  //获取类实例的@MappedTypes标签
  MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
  if (mappedTypes != null) {
    //如果打上了该标签,遍历标签的value(该value为一个Class数组)
    for (Class<?> javaTypeClass : mappedTypes.value()) {
      //注册java类型,以及类型处理器类数据库字段类型(通过@MappedJdbcTypes标签识别),类型处理器到集合中
      register(javaTypeClass, typeHandlerClass);
      //表示发现了java类型与数据库字段类型的映射
      mappedTypeFound = true;
    }
  }
  //如果未发现java类型与数据库字段类型的映射
  if (!mappedTypeFound) {
    //注册null类型,空的数据库类型以及类型处理器类实例以无参构造器进行构建的类型处理器对象到集合中
    register(getInstance(null, typeHandlerClass));
  }
}
代码语言:javascript
复制
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}
代码语言:javascript
复制
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  //如果构造参数类实例不为null
  if (javaTypeClass != null) {
    try {
      //获取单个类实例的类型处理器构造器
      Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
      //返回以单参类型处理器构造器构造的类型处理器实例对象
      return (TypeHandler<T>) c.newInstance(javaTypeClass);
    } catch (NoSuchMethodException ignored) {
      // ignored
    } catch (Exception e) {
      throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
    }
  }
  //如果构造参数类实例为null
  try {
    //获取无参类型处理器构造器
    Constructor<?> c = typeHandlerClass.getConstructor();
    //返回无参类型处理器构造器构造的类型处理器实例对象
    return (TypeHandler<T>) c.newInstance();
  } catch (Exception e) {
    throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
  }
}
代码语言:javascript
复制
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  //获取类型处理器类实例的@MappedJdbcTypes标签
  MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
  if (mappedJdbcTypes != null) {
    //如果该类打上了该标签,遍历该标签的value(value为JdbcType数组)
    for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
      //将java类型与每一个数据库类型注册到集合中
      register(javaType, handledJdbcType, typeHandler);
    }
    //如果该标签包含空的数据库类型,默认为false
    if (mappedJdbcTypes.includeNullJdbcType()) {
      //将java类型与空的数据库类型注册到集合中
      register(javaType, null, typeHandler);
    }
  } else { //如果该类没有打上@MappedJdbcTypes标签,将java类型与空的数据库类型注册到集合中
    register(javaType, null, typeHandler);
  }
}
代码语言:javascript
复制
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) {
    //如果java类型不为null,从java与数据库字段类型映射中拿区该java类型的数据库字段与其处理器之间的映射map
    Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
    //如果该映射为null或者是一个空映射
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      //生成新的映射对象
      map = new HashMap<JdbcType, TypeHandler<?>>();
      //将该新的对象与java类型放入java与数据库字段类型映射中
      TYPE_HANDLER_MAP.put(javaType, map);
    }
    //将数据库字段类型与类型处理器放入该映射中
    map.put(jdbcType, handler);
  }
  //将类型处理器的类实例与类型处理器对象添加到所有类型与处理器之间的映射中
  ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
代码语言:javascript
复制
public <T> void register(TypeHandler<T> typeHandler) {
  boolean mappedTypeFound = false;
  //获取类型处理器的@MappedTypes标签
  MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
  if (mappedTypes != null) {
    //如果打上了该标签,遍历该标签的value(value为一个Class数组)
    for (Class<?> handledType : mappedTypes.value()) {
      //注册处理类型,以及类型处理器类数据库字段类型(通过@MappedJdbcTypes标签识别),类型处理器到集合中
      register(handledType, typeHandler);
      //表示处理类型与数据库字段类型映射已经发现
      mappedTypeFound = true;
    }
  }
  // @since 3.1.0 - try to auto-discover the mapped type
  //如果处理类型与数据库字段类型映射没有被发现且类型处理器对象属于TypeReference实例
  if (!mappedTypeFound && typeHandler instanceof TypeReference) {
    try {
      //将类型处理器对象强制转换成类型引用对象
      TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
      //注册该引用对象引用的原生类型,以及类型处理器类数据库字段类型(通过@MappedJdbcTypes标签识别),类型处理器到集合中
      register(typeReference.getRawType(), typeHandler);
      //表示引用对象引用的原生类型与数据库字段类型映射被发现
      mappedTypeFound = true;
    } catch (Throwable t) {
      // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
    }
  }
  //如果以上都没发现映射关系
  if (!mappedTypeFound) {
    //注册null类型与空数据库字段类型与类型处理器对象到集合中
    register((Class<T>) null, typeHandler);
  }
}

XMLMapperBuilder也是BaseBuilder抽象类的子类之一,负责解析映射配置文件,在XMLConfigBuilder.mapperElement()方法中被实例化。比如有一份mapper配置文件

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cloud.user.dao.BaseDao">
    <insert id="insert" parameterType="com.cloud.model.base.BaseEntity">
        INSERT INTO ${tableName} (
        <if test="idNames != null and idNames.length > 0">
            <foreach collection="idNames" index="index" item="idName" separator=",">
                ${idName}
            </foreach>
        </if>
        <if test="idNames != null and idNames.length > 0 and columnNames != null and columnNames.length > 0">
            ,
        </if>
        <if test="columnNames != null and columnNames.length > 0">
            <foreach collection="columnNames" index="index" item="columnName" separator=",">
                ${columnName}
            </foreach>
        </if>
        ) VALUES (
        <if test="idValues != null and idValues.length > 0">
            <foreach collection="idValues" index="index" item="idValue" separator=",">
                #{idValue}
            </foreach>
        </if>
        <if test="idValues != null and idValues.length > 0 and columnValues != null and columnValues.length > 0">
            ,
        </if>
        <if test="columnValues != null and columnValues.length > 0">
            <foreach collection="columnValues" index="index" item="columnValue" separator=",">
                #{columnValue}
            </foreach>
        </if>
        )
    </insert>
    <update id="update" parameterType="com.cloud.model.base.BaseEntity">
        UPDATE ${tableName}
        <set>
            <foreach collection="columnMap" index="columnKey" item="columnValue" separator=",">
                ${columnKey}=#{columnValue}
            </foreach>
        </set>
        <where>
            <foreach collection="idMap" index="idKey" item="idValue" separator="AND">
                ${idKey}=#{idValue}
            </foreach>
        </where>
    </update>
    <delete id="delete" parameterType="com.cloud.model.base.BaseEntity">
        DELETE FROM ${tableName}
        <where>
            <foreach collection="idMap" index="idKey" item="idValue" separator="AND">
                ${idKey}=#{idValue}
            </foreach>
        </where>
    </delete>
</mapper>

XMLMapperBuilder.parse()方法是解析映射文件的入口

代码语言:javascript
复制
private final MapperBuilderAssistant builderAssistant; //建造者助理对象
代码语言:javascript
复制
private final Map<String, XNode> sqlFragments; //sql片段映射
代码语言:javascript
复制
public void parse() {
  //判断是否已经加载过该映射文件
  if (!configuration.isResourceLoaded(resource)) {
    //解析<mapper>节点
    configurationElement(parser.evalNode("/mapper"));
    //将该映射文件放入全局配置的已载入集合中
    configuration.addLoadedResource(resource);
    //注册Mapper接口
    bindMapperForNamespace();
  }
  //处理configurationElement()方法中解析失败的<resultMap>节点
  parsePendingResultMaps();
  //处理configurationElement()方法中解析失败的<cache-ref>节点
  parsePendingCacheRefs();
  //处理configurationElement()方法中解析失败的SQL语句节点
  parsePendingStatements();
}
代码语言:javascript
复制
private void configurationElement(XNode context) {
  try {
    //获取<mapper>节点的namespace属性
    String namespace = context.getStringAttribute("namespace");
    //如果该namespace属性为空则抛出异常
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    //将该namespace属性放入助理对象的当前namespace属性
    builderAssistant.setCurrentNamespace(namespace);
    //解析<cache-ref>节点
    cacheRefElement(context.evalNode("cache-ref"));
    //解析<cache>节点
    cacheElement(context.evalNode("cache"));
    //解析<parameterMap>节点(以废弃,不做代码分析)
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //解析<resultMap>节点
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    //解析<sql>节点
    sqlElement(context.evalNodes("/mapper/sql"));
    //解析<select>、<insert>、<update>、<delete>节点
    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);
  }
}
代码语言:javascript
复制
private void cacheRefElement(XNode context) {
  if (context != null) {
    //如果<cache-ref>节点不为null,将当前命名空间与节点取得的namespace属性放入到命名空间缓存中
    configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
    //使用助理对象和节点的namespace属性来构造一个CacheRefResolver对象
    CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
    try {
      //从全局配置信息中获取缓存
      cacheRefResolver.resolveCacheRef();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteCacheRef(cacheRefResolver);
    }
  }
}
代码语言:javascript
复制
private void cacheElement(XNode context) throws Exception {
  if (context != null) {
    //获取<cache>节点的type属性,默认值是PERPETUAL
    String type = context.getStringAttribute("type", "PERPETUAL");
    //从类型别名映射中取出该type属性所对应的缓存类实例
    Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    //获取<cache>节点的eviction属性,默认值是LRU
    String eviction = context.getStringAttribute("eviction", "LRU");
    //从类型别名映射中取出该eviction属性对应的缓存类实例
    Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
    //获取<cache>节点的flushInterval属性
    Long flushInterval = context.getLongAttribute("flushInterval");
    //获取<cache>节点的size属性
    Integer size = context.getIntAttribute("size");
    //获取<cache>节点的readOnly属性,默认值false
    boolean readWrite = !context.getBooleanAttribute("readOnly", false);
    //获取<cache>节点的blocking属性,默认值false
    boolean blocking = context.getBooleanAttribute("blocking", false);
    //获取<cache>下的所有子节点,并转化为一个Properties对象
    Properties props = context.getChildrenAsProperties();
    //通过以上属性生成Cache实例对象,并添加到全局配置信息中
    builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
  }
}
代码语言:javascript
复制
private void resultMapElements(List<XNode> list) throws Exception {
  //遍历所有的<resultMap>节点(因为<resultMap>节点可能不止一个)
  for (XNode resultMapNode : list) {
    try {
      resultMapElement(resultMapNode);
    } catch (IncompleteElementException e) {
      // ignore, it will be retried
    }
  }
}
代码语言:javascript
复制
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
  return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
}
代码语言:javascript
复制
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
  //将节点的id值放入错误上下文的activity属性
  ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
  //获取<resultMap>节点的id属性, 默认值会拼装所有父节点的 id 或 value 或 property 
  String id = resultMapNode.getStringAttribute("id",
      resultMapNode.getValueBasedIdentifier());
  //获取<resultMap>节点的type属性,默认值会被指定为ofType属性,resultType属性,javaType属性
  String type = resultMapNode.getStringAttribute("type",
      resultMapNode.getStringAttribute("ofType",
          resultMapNode.getStringAttribute("resultType",
              resultMapNode.getStringAttribute("javaType"))));
  //获取<resultMap>节点的extends属性
  String extend = resultMapNode.getStringAttribute("extends");
  //获取<resultMap>节点的autoMapping属性
  Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
  //通过类型别名映射获取type属性为名称的Class实例
  Class<?> typeClass = resolveClass(type);
  Discriminator discriminator = null;
  //定义一个ResultMapping列表(ResultMapping代表<resultMap>节点下的所有子节点,属性定义见后面),该集合用于记录解析的结果
  List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
  //将额外的resultMapping集合添加进来
  resultMappings.addAll(additionalResultMappings);
  //获取<resultMap>的子节点
  List<XNode> resultChildren = resultMapNode.getChildren();
  //遍历所有子节点
  for (XNode resultChild : resultChildren) {
    //如果子节点的名称为constructor
    if ("constructor".equals(resultChild.getName())) {
      //解析<constructor>子节点
      processConstructorElement(resultChild, typeClass, resultMappings);
    //如果子节点的名称为discriminator
    } else if ("discriminator".equals(resultChild.getName())) {
      //解析<discriminator>子节点
      discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
    } else {
      //处理<id>、<result>、<association>、<collection>等节点
      List<ResultFlag> flags = new ArrayList<ResultFlag>();
      if ("id".equals(resultChild.getName())) {
        flags.add(ResultFlag.ID); //如果是<id>节点,向flags集合中添加ID枚举
      }
      //创建ResultMapping对象,并添加到集合中
      resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
    }
  }
  ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  try {
    //创建ResultMap对象,并添加到全局配置信息中,ResultMap对象的属性信息见后面的代码
    return resultMapResolver.resolve();
  } catch (IncompleteElementException  e) {
    configuration.addIncompleteResultMap(resultMapResolver);
    throw e;
  }
}
代码语言:javascript
复制
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
  //获取<constructor>的所有子节点
  List<XNode> argChildren = resultChild.getChildren();
  //遍历<constructor>的子节点
  for (XNode argChild : argChildren) {
    //产生一个结果标志列表对象
    List<ResultFlag> flags = new ArrayList<ResultFlag>();
    //将结果标志之一的CONSTRUCTOR标志(ResultFlag是一个枚举)加入结果标志列表中
    flags.add(ResultFlag.CONSTRUCTOR);
    //如果子节点的名称为idArg
    if ("idArg".equals(argChild.getName())) {
      //将ID标志加入结果标志列表中
      flags.add(ResultFlag.ID);
    }
    //根据子节点的各个属性构建ResultMapping对象,并添加到集合中
    resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
  }
}
代码语言:javascript
复制
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
  String property;
  //如果标志列表中包含CONSTRUCTOR
  if (flags.contains(ResultFlag.CONSTRUCTOR)) {
    //获取节点的name属性
    property = context.getStringAttribute("name");
  } else {
    //否则获取节点的property属性
    property = context.getStringAttribute("property");
  }
  //获取节点的column属性
  String column = context.getStringAttribute("column");
  //获取节点的javaType属性
  String javaType = context.getStringAttribute("javaType");
  //获取节点的jdbcType属性
  String jdbcType = context.getStringAttribute("jdbcType");
  //获取节点的select属性
  String nestedSelect = context.getStringAttribute("select");
  //获取节点的resultMap属性,默认值为嵌套的resultMap的Id值
  String nestedResultMap = context.getStringAttribute("resultMap",
      processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
  //获取节点的notNullColumn属性
  String notNullColumn = context.getStringAttribute("notNullColumn");
  //获取节点的columnPrefix属性
  String columnPrefix = context.getStringAttribute("columnPrefix");
  //获取节点的typeHandler属性
  String typeHandler = context.getStringAttribute("typeHandler");
  //获取节点的resultSet属性
  String resultSet = context.getStringAttribute("resultSet");
  //获取节点的foreignColumn属性
  String foreignColumn = context.getStringAttribute("foreignColumn");
  //获取节点的tetchType属性
  boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
  //在类型别名映射中获取javaType属性对应的Java类实例
  Class<?> javaTypeClass = resolveClass(javaType);
  //在类型别名映射中获取typeHandler属性对应的类型处理器类实例
  @SuppressWarnings("unchecked")
  Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
  //在类型别名映射中获取jdbcType属性对应的数据库字段类型的枚举
  JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
  //创建ResultMapping对象
  return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
代码语言:javascript
复制
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception {
  //如果该节点名称为assoclation或者collection或者case
  if ("association".equals(context.getName())
      || "collection".equals(context.getName())
      || "case".equals(context.getName())) {
    //如果该节点的select属性为null
    if (context.getStringAttribute("select") == null) {
      //解析该节点,获取解析结果
      ResultMap resultMap = resultMapElement(context, resultMappings);
      //返回该解析结果的Id
      return resultMap.getId();
    }
  }
  return null;
}
代码语言:javascript
复制
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
  //获取<discriminator>节点的column属性
  String column = context.getStringAttribute("column");
  //获取<discriminator>节点的javaType属性
  String javaType = context.getStringAttribute("javaType");
  //获取<discriminator>节点的jdbcType属性
  String jdbcType = context.getStringAttribute("jdbcType");
  //获取<discriminator>节点的typeHandler属性
  String typeHandler = context.getStringAttribute("typeHandler");
  //从类型别名映射中获取javaType属性对应的java类型类实例
  Class<?> javaTypeClass = resolveClass(javaType);
  //从类型别名映射中获取typeHandler属性对应的类型处理器类实例
  @SuppressWarnings("unchecked")
  Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
  //从类型别名映射中获取数据库字段类型的枚举
  JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
  Map<String, String> discriminatorMap = new HashMap<String, String>();
  //遍历<discriminator>节点的所有子节点
  for (XNode caseChild : context.getChildren()) {
    //获取子节点的value属性
    String value = caseChild.getStringAttribute("value");
    //获取子节点的resultMap属性,默认为嵌套的ResultMap的Id值
    String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
    //将value属性以及resultMap属性添加到集合中
    discriminatorMap.put(value, resultMap);
  }
  //创建鉴别器(Discriminator)对象
  return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}
代码语言:javascript
复制
private void sqlElement(List<XNode> list) throws Exception {
  //如果从全局配置信息中获取的数据库Id不为null
  if (configuration.getDatabaseId() != null) {
    sqlElement(list, configuration.getDatabaseId());
  }
  sqlElement(list, null);
}
代码语言:javascript
复制
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
  //遍历所有的<sql>节点(因为<sql>节点可能不止1个)
  for (XNode context : list) {
    //获取<sql>节点的databaseId属性
    String databaseId = context.getStringAttribute("databaseId");
    //获取<sql>节点的id属性
    String id = context.getStringAttribute("id");
    //为id添加命名空间
    id = builderAssistant.applyCurrentNamespace(id, false);
    //检测<sql>的数据库id与全局配置信息中的数据库id是否一致
    if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
      //如果一致将id与该<sql>节点添加到sql片段映射中
      sqlFragments.put(id, context);
    }
  }
}
代码语言:javascript
复制
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
  //如果全局配置信息中的数据库id不为null
  if (requiredDatabaseId != null) {
    //如果从<sql>标签中获取的数据库id与全局配置信息中的数据库id不同,返回false
    if (!requiredDatabaseId.equals(databaseId)) {
      return false;
    }
  } else { //如果全局配置信息中的数据库id为null
    //从<sql>标签中获取的数据库id也为null,返回false
    if (databaseId != null) {
      return false;
    }
    //如果当前sql片段映射中包含id
    if (this.sqlFragments.containsKey(id)) {
      //获取该id对应的<sql>节点
      XNode context = this.sqlFragments.get(id);
      //如果该<sql>节点获取的数据库id属性不为null,返回false
      if (context.getStringAttribute("databaseId") != null) {
        return false;
      }
    }
  }
  return true;
}
代码语言:javascript
复制
private void buildStatementFromContext(List<XNode> list) {
  //如果从全局配置信息中获取的数据库id不为null
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  buildStatementFromContext(list, null);
}
代码语言:javascript
复制
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  //遍历所有的<select>、<insert>、<update>、<delete>节点
  for (XNode context : list) {
    //创建XMLStatementBuilder对象
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      //使用该对象来解析<select>、<insert>、<update>、<delete>节点
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}
代码语言:javascript
复制
private void bindMapperForNamespace() {
  //获取当前的命名空间
  String namespace = builderAssistant.getCurrentNamespace();
  //如果当前命名空间不为null
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      //反射加载命名空间的类实例
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    //如果该类实例不为null
    if (boundType != null) {
      //如果全局配置信息中没有该类实例的映射
      if (!configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        //全局配置信息中添加该命名空间
        configuration.addLoadedResource("namespace:" + namespace);
        //全局配置信息中添加该类实例的映射
        configuration.addMapper(boundType);
      }
    }
  }
}
代码语言:javascript
复制
private void parsePendingResultMaps() {
  //从全局配置信息中获取解析错误的ResultMaps集合
  Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
  synchronized (incompleteResultMaps) { //同步该集合
    //获取该集合的迭代器
    Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
    while (iter.hasNext()) {
      try {
        //生成新的ResultMap,加入到全局配置信息中
        iter.next().resolve();
        //从解析错误的集合中移除
        iter.remove();
      } catch (IncompleteElementException e) {
        // ResultMap is still missing a resource...
      }
    }
  }
}
代码语言:javascript
复制
private void parsePendingCacheRefs() {
  //从全局配置信息中获取解析错误的CacheRefs集合
  Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
  synchronized (incompleteCacheRefs) { //同步该集合
    //获取该集合的迭代器
    Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
    while (iter.hasNext()) {
      try {
        //产生新的缓存对象
        iter.next().resolveCacheRef();
        //从解析错误的集合中移除
        iter.remove();
      } catch (IncompleteElementException e) {
        // Cache ref is still missing a resource...
      }
    }
  }
}
代码语言:javascript
复制
private void parsePendingStatements() {
  //从全局配置信息中获取解析失败的SQL语句集合
  Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
  synchronized (incompleteStatements) { //同步该集合
    //获取该集合的迭代器
    Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
    while (iter.hasNext()) {
      try {
        //生成新的MappedStatement对象,并添加到全局配置信息中
        iter.next().parseStatementNode();
        //从解析失败的集合中移除
        iter.remove();
      } catch (IncompleteElementException e) {
        // Statement is still missing a resource...
      }
    }
  }
}

在Configuration中

代码语言:javascript
复制
protected final Map<String, String> cacheRefMap = new HashMap<String, String>(); //实际绑定到命名空间的缓存
代码语言:javascript
复制
public void addCacheRef(String namespace, String referencedNamespace) {
  cacheRefMap.put(namespace, referencedNamespace);
}

在CacheRefResolver中

代码语言:javascript
复制
public Cache resolveCacheRef() {
  return assistant.useCacheRef(cacheRefNamespace);
}

在MapperBuilderAssistant中

代码语言:javascript
复制
private boolean unresolvedCacheRef; //未解决的缓存方案
代码语言:javascript
复制
private Cache currentCache; //当前缓存
代码语言:javascript
复制
public Cache useCacheRef(String namespace) {
  //如果命名空间为null,抛出异常
  if (namespace == null) {
    throw new BuilderException("cache-ref element requires a namespace attribute.");
  }
  try {
    //设置当前缓存方案未解决
    unresolvedCacheRef = true;
    //从全局配置信息中获取命名空间的缓存
    Cache cache = configuration.getCache(namespace);
    //如果获取的缓存为null,抛出异常
    if (cache == null) {
      throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
    }
    //设置当前缓存为获取到的缓存
    currentCache = cache;
    //设置当前缓存方案已解决
    unresolvedCacheRef = false;
    return cache;
  } catch (IllegalArgumentException e) {
    throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
  }
}
代码语言:javascript
复制
public Cache useNewCache(Class<? extends Cache> typeClass,
    Class<? extends Cache> evictionClass,
    Long flushInterval,
    Integer size,
    boolean readWrite,
    boolean blocking,
    Properties props) {
  //通过<cache>节点配置的属性来创建Cache实例
  Cache cache = new CacheBuilder(currentNamespace)
      .implementation(valueOrDefault(typeClass, PerpetualCache.class))
      .addDecorator(valueOrDefault(evictionClass, LruCache.class))
      .clearInterval(flushInterval)
      .size(size)
      .readWrite(readWrite)
      .blocking(blocking)
      .properties(props)
      .build();
  //将该缓存对象添加到全局配置信息中
  configuration.addCache(cache);
  currentCache = cache;
  return cache;
}
代码语言:javascript
复制
public String applyCurrentNamespace(String base, boolean isReference) {
  //如果id本身为null,返回null
  if (base == null) {
    return null;
  }
  //是否已经被标记包含了命名空间,如果包含则直接返回id
  if (isReference) {
    // is it qualified with any namespace yet?
    if (base.contains(".")) {
      return base;
    }
  } else { 
    // is it qualified with this namespace yet?
    //如果id以命名空间开头,直接返回
    if (base.startsWith(currentNamespace + ".")) {
      return base;
    }
    //如果id不包含命名空间却有.号,抛出异常
    if (base.contains(".")) {
      throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
    }
  }
  //给id加上命名空间开头
  return currentNamespace + "." + base;
}

在ResultMapping中

代码语言:javascript
复制
private Configuration configuration; //全局配置信息
private String property; //对应节点的property属性,表示与column进行映射的属性
private String column; //对应节点的column属性,表示的是从数据库中得到的列名或是列名的别名
private Class<?> javaType; //对应节点的javaType属性,表示的是一个Java类型的类实例
private JdbcType jdbcType; //对应节点的jdbcType属性,表示的是进行映射的列的数据库字段类型
private TypeHandler<?> typeHandler; //对应节点的typeHandler属性,表示的是类型处理器
//对应节点的resultMap属性,该属性通过id引用了另一个<resultMap>节点定义,它负责将结果集中的一部分列映射成其他关联的结果对象。这样我们可以通过join方式进行
//关联查询,然后直接映射成多个对象,并同时设置这些对象之间的组合关系
private String nestedResultMapId; 
//对应节点的select属性,该属性通过id引用了另一个<select>节点定义,它会把指定的列的值传入select属性指定的select语句中作为参数进行查询
private String nestedQueryId;
private Set<String> notNullColumns; //对应节点的notNullColumn属性拆分后的结果
private String columnPrefix; //对应节点的columnPrefix属性
private List<ResultFlag> flags; //处理后的标志,标志共2个:id和constructor
private List<ResultMapping> composites; //对应节点的column属性拆分后生成的结果
private String resultSet; //对应节点的resultSet属性
private String foreignColumn; //对应节点的foreignColumn属性
private boolean lazy; //是否延迟加载,对应节点的fetchType属性

它跟mapper文件的关系如下图所示

图片来源于网络

在ResultMap中

代码语言:javascript
复制
private Configuration configuration; //全局配置信息

private String id; //<resultMap>节点的id属性
private Class<?> type; //<resultMap>节点的type属性
private List<ResultMapping> resultMappings; //记录除了<discriminator>节点之外的其他映射关系(即ResultMapping对象集合)
private List<ResultMapping> idResultMappings; //记录了映射关系中带有ID标志的映射关系,例如<id>节点和<constructor>节点的<idArg>子节点
private List<ResultMapping> constructorResultMappings; //记录了映射关系中带有Constructor标志的映射关系,例如<constructor>所有子元素
private List<ResultMapping> propertyResultMappings; //记录了映射关系中不带有Constructor标志的映射关系
private Set<String> mappedColumns; //记录了所有映射关系中涉及的column属性的集合
private Set<String> mappedProperties; //记录了所有映射关系中涉及的properties属性的集合
private Discriminator discriminator; //鉴别器,对应<discriminator>节点
private boolean hasNestedResultMaps; //是否含有嵌套的结果映射,如果某个映射关系中存在resultMap属性,且不存在resultSet属性,则为true
private boolean hasNestedQueries; //是否含有嵌套查询,如果某个属性映射存在select属性,则为true
private Boolean autoMapping; //是否开启自动映射

XMLStatementBuilder也是BaseBuilder抽象类的子类之一,负责解析映射配置文件中的<select>、<insert>、<update>、<delete>节点,在XMLMapperBuilder.buildStatementFromContext()方法中被实例化。

解析SQL节点的代码如下

代码语言:javascript
复制
private final String requiredDatabaseId; //全局配置信息中获取的数据库id
代码语言:javascript
复制
public void parseStatementNode() {
  //获取该节点的id属性
  String id = context.getStringAttribute("id");
  //获取该节点的databaseId属性
  String databaseId = context.getStringAttribute("databaseId");
  //判断全局配置信息中的数据库id与该节点的数据库id如果不同,结束
  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }
  //获取该节点的fetchSize属性
  Integer fetchSize = context.getIntAttribute("fetchSize");
  //获取该节点的timeout属性
  Integer timeout = context.getIntAttribute("timeout");
  //获取该节点的parameterMap属性
  String parameterMap = context.getStringAttribute("parameterMap");
  //获取该节点的parameterType属性
  String parameterType = context.getStringAttribute("parameterType");
  //在类型别名映射中获取parameterType属性的Class实例
  Class<?> parameterTypeClass = resolveClass(parameterType);
  //获取该节点的resultMap属性
  String resultMap = context.getStringAttribute("resultMap");
  //获取该节点的resultType属性
  String resultType = context.getStringAttribute("resultType");
  //获取该节点的lang属性
  String lang = context.getStringAttribute("lang");
  //从全局配置信息中获取lang属性Class实例对应的LanguageDriver对象
  LanguageDriver langDriver = getLanguageDriver(lang);
  //从类型别名映射中获取resultType属性的类实例
  Class<?> resultTypeClass = resolveClass(resultType);
  //获取该节点的resultSetType属性
  String resultSetType = context.getStringAttribute("resultSetType");
  //从该节点的statementType属性(默认值为PREPARED)得到StatementType的枚举
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  //从resultSetType属性获取ResultSetType的枚举
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  //获取节点的节点名
  String nodeName = context.getNode().getNodeName();
  //获取节点名的枚举
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  //是否为<select>节点
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  //获取该节点的flushCache属性,默认值为非isSelect
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  //获取该节点的useCache属性,默认值为isSelect
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  //获取该节点的resultOrdered属性,默认值为false
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  //在解析SQL节点之前,先处理其中的<include>节点
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  //解析<selectKey>节点
  processSelectKeyNodes(id, parameterTypeClass, langDriver);
  
  //获取该节点的SQL语句对象(已经移除了<include><selectKey>节点的)
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  //获取该节点的resultSets属性
  String resultSets = context.getStringAttribute("resultSets");
  //获取该节点的keyProperty属性
  String keyProperty = context.getStringAttribute("keyProperty");
  //获取该节点的keyColumn属性
  String keyColumn = context.getStringAttribute("keyColumn");
  KeyGenerator keyGenerator;
  //获取<selectKey>节点对应的SelectKeyGenerator的id
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  //如果全局配置信息中有该id
  if (configuration.hasKeyGenerator(keyStatementId)) {
    //获取该id对应的主键对象
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    //否则根据节点的useGeneratedKeys属性的默认值来判断有无主键
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }
  //创建MappedStatement对象,并添加到全局配置信息中保存
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered, 
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
代码语言:javascript
复制
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
  //如果全局配置信息中的数据库id不为null
  if (requiredDatabaseId != null) {
    //如果SQL语句节点中的数据库id与全局配置信息中的数据库id不同,返回false
    if (!requiredDatabaseId.equals(databaseId)) {
      return false;
    }
  } else { //如果全局配置信息中的数据库id为null
    //且SQL语句节点的数据库id也为null,返回false
    if (databaseId != null) {
      return false;
    }
    //绑定SQL语句的id与命名空间
    id = builderAssistant.applyCurrentNamespace(id, false);
    //如果全局配置信息中有该id的Statement
    if (this.configuration.hasStatement(id, false)) {
      //从全局配置信息中获取该id对应的Statement
      MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
      //如果该Statement的数据库id不为null,返回false
      if (previous.getDatabaseId() != null) {
        return false;
      }
    }
  }
  return true;
}
代码语言:javascript
复制
private LanguageDriver getLanguageDriver(String lang) {
  Class<?> langClass = null;
  if (lang != null) {
    //如果SQL语句节点的lang属性不为null,从类型别名映射中获取lang属性的Class实例
    langClass = resolveClass(lang);
  }
  //从全局配置信息中返回该类实例对应的LanguageDriver对象
  return builderAssistant.getLanguageDriver(langClass);
}
代码语言:javascript
复制
private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
  //获取所有的<selectkey>节点
  List<XNode> selectKeyNodes = context.evalNodes("selectKey");
  //如果全局配置信息的数据库Id不为null
  if (configuration.getDatabaseId() != null) {
    parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
  }
  parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
  removeSelectKeyNodes(selectKeyNodes);
}
代码语言:javascript
复制
private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
  //遍历所有的<selectkey>节点
  for (XNode nodeToHandle : list) {
    //通过<select>节点id+!selectKey组成id
    String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    //获取该节点的databasedId属性
    String databaseId = nodeToHandle.getStringAttribute("databaseId");
    //如果databasedId属性与全局配置信息中的数据库Id相同
    if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
      //解析<selectKey>节点
      parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
    }
  }
}
代码语言:javascript
复制
private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
  //获取节点的resultType属性
  String resultType = nodeToHandle.getStringAttribute("resultType");
  //在类型别名映射中获取resultType属性对应的类实例
  Class<?> resultTypeClass = resolveClass(resultType);
  //获取节点的statementType的属性(默认为PREPARED)的枚举
  StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  //获取节点的keyProperty属性
  String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
  //获取节点的keyColumn属性
  String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
  //判断节点的order属性(默认为AFTER)是否为BEFORE
  boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));

  //defaults
  boolean useCache = false;
  boolean resultOrdered = false;
  KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
  Integer fetchSize = null;
  Integer timeout = null;
  boolean flushCache = false;
  String parameterMap = null;
  String resultMap = null;
  ResultSetType resultSetTypeEnum = null;
  //获取该节点的SQL语句
  SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
  //确定Sql命令类型为select
  SqlCommandType sqlCommandType = SqlCommandType.SELECT;
  //根据以上所有信息产生一个MappedStatement对象,并添加到全局信息配置中(MappedStatement是一个记录了SQL语句节点所有信息的类)
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null);
  //将SQL语句节点的id与命名空间绑定
  id = builderAssistant.applyCurrentNamespace(id, false);
  //从全局配置信息中获取该id对应的MappedStatement对象
  MappedStatement keyStatement = configuration.getMappedStatement(id, false);
  //创建<selectKey>节点对应的KeyGennerator,添加到全局配置信息的keyGenerators集合中保存
  configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
}

在MapperBuilderAssistant中

代码语言:javascript
复制
public LanguageDriver getLanguageDriver(Class<?> langClass) {
  if (langClass != null) {
    //如果该langClass类实例不为null,将该类实例注册到全局配置信息中
    configuration.getLanguageRegistry().register(langClass);
  } else {
    //如果为null,从全局配置信息的注册器中获取默认的类实例
    langClass = configuration.getLanguageRegistry().getDefaultDriverClass();
  }
  //返回从全局配置信息中该类实例对应的LanguageDriver对象
  return configuration.getLanguageRegistry().getDriver(langClass);
}

在XMLIncludeTransformer中

这里是针对<include>节点的,我们假设有这样一段XML配置

代码语言:javascript
复制
<!-- assist是查询辅助工具类, ${req.require}表示列名,#{req.value}表示值它是防SQL注入的 -->
<sql id="assist">
   <where>
      <foreach collection="require" item="req" separator=" ">
         ${req.require}
         <if test="req.value != null">
            #{req.value}
         </if>
         <if test="req.values != null">
            <foreach collection="req.values" item="value" separator=",">
               #{value}
            </foreach>
         </if>
         <if test="req.suffix != null"> ${req.suffix}</if>
      </foreach>
   </where>
</sql>
代码语言:javascript
复制
<select id="getBetdetailsRowCount" parameterType="com.cloud.model.common.Assist" resultType="java.lang.Long">
   select count(*) from betdetails
   <if test="require!=null">
      <include refid="assist" />
   </if>
</select>
代码语言:javascript
复制
public void applyIncludes(Node source) {
  Properties variablesContext = new Properties();
  Properties configurationVariables = configuration.getVariables();
  if (configurationVariables != null) {
    variablesContext.putAll(configurationVariables);
  }
  applyIncludes(source, variablesContext, false);
}
代码语言:javascript
复制
private void applyIncludes(Node source, final Properties variablesContext, boolean included) {
  //如果节点名称为include
  if (source.getNodeName().equals("include")) {
    //查找refid属性指向的<sql>节点,返回的是其深克隆的Node对象
    Node toInclude = findSqlFragment(getStringAttribute(source, "refid"), variablesContext);
    //解析<include>节点下的<property>节点,并得到一个新的Properties对象
    Properties toIncludeContext = getVariablesContext(source, variablesContext);
    //递归处理<include>节点,在<sql>节点中可能会使用<include>引用了其他SQL片段
    applyIncludes(toInclude, toIncludeContext, true);
    //如果<sql>节点跟<include>节点不是同一个XML里的节点(顶层节点不同)
    if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
      //将<sql>节点导入到<include>所在的顶层节点(<mapper>节点),变成一个新节点
      toInclude = source.getOwnerDocument().importNode(toInclude, true);
    }
    //将<include>节点替换成<sql>节点
    source.getParentNode().replaceChild(toInclude, source);
    //如果<sql>节点有子节点
    while (toInclude.hasChildNodes()) {
      //将子节点添加到<sql>节点前面
      toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);
    }
    //移除<sql>节点本身
    toInclude.getParentNode().removeChild(toInclude);
    //如果该节点不是<include>节点,且是一个元素类型的节点(一般为SQL语句的节点)
  } else if (source.getNodeType() == Node.ELEMENT_NODE) {
    //如果从全局配置信息中获取的变量属性不为空,且<include>节点已经被替换成SQL语句节点
    if (included && !variablesContext.isEmpty()) {
      //获取该节点的所有属性
      NamedNodeMap attributes = source.getAttributes();
      //遍历该节点的所有属性
      for (int i = 0; i < attributes.getLength(); i++) {
        Node attr = attributes.item(i);
        //将该属性值替换变量
        attr.setNodeValue(PropertyParser.parse(attr.getNodeValue(), variablesContext));
      }
    }
    NodeList children = source.getChildNodes();
    //遍历该SQL语句的子节点
    for (int i = 0; i < children.getLength(); i++) {
      //递归处理这些子节点
      applyIncludes(children.item(i), variablesContext, included);
    }
    //如果<included>已经被替换成SQL语句节点且该节点类型为文本
  } else if (included && source.getNodeType() == Node.TEXT_NODE
      && !variablesContext.isEmpty()) {
    //使用之前解析得到的Properties对象替换对应的占位符(替换变量)
    source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
  }
}
代码语言:javascript
复制
private Node findSqlFragment(String refid, Properties variables) {
  //去除refid字符串中前后限定符中的\
  refid = PropertyParser.parse(refid, variables);
  //将refid与命名空间绑定
  refid = builderAssistant.applyCurrentNamespace(refid, true);
  try {
    //从refid获取在全局配置信息中对应的<sql>节点
    XNode nodeToInclude = configuration.getSqlFragments().get(refid);
    //返回获取的<sql>节点的克隆
    return nodeToInclude.getNode().cloneNode(true);
  } catch (IllegalArgumentException e) {
    throw new IncompleteElementException("Could not find SQL statement to include with refid '" + refid + "'", e);
  }
}
代码语言:javascript
复制
private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {
  Map<String, String> declaredProperties = null;
  NodeList children = node.getChildNodes();
  //遍历<include>节点的所有子节点
  for (int i = 0; i < children.getLength(); i++) {
    Node n = children.item(i);
    //如果该子节点类型是一个元素
    if (n.getNodeType() == Node.ELEMENT_NODE) {
      //获取该子节点的name属性
      String name = getStringAttribute(n, "name");
      //获取该子节点的value属性(替换了变量)
      String value = PropertyParser.parse(getStringAttribute(n, "value"), inheritedVariablesContext);
      if (declaredProperties == null) {
        declaredProperties = new HashMap<String, String>();
      }
      //如果declaredProperties的key已经存在,抛出异常,即子节点的名称不能重复
      if (declaredProperties.put(name, value) != null) {
        throw new BuilderException("Variable " + name + " defined twice in the same include definition");
      }
    }
  }
  //如果没有子节点
  if (declaredProperties == null) {
    //返回从全局配置信息中获取的变量属性
    return inheritedVariablesContext;
  } else { //如果有子节点
    //将全局配置信息中获取的变量属性与<include>子节点获取的name,value属性映射放入一个属性对象中返回
    Properties newProperties = new Properties();
    newProperties.putAll(inheritedVariablesContext);
    newProperties.putAll(declaredProperties);
    return newProperties;
  }
}

在PropertyParser中

代码语言:javascript
复制
public static String parse(String string, Properties variables) {
  VariableTokenHandler handler = new VariableTokenHandler(variables);
  GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
  return parser.parse(string);
}

在GenericTokenParser中

代码语言:javascript
复制
public String parse(String text) {
  if (text == null || text.isEmpty()) {
    return "";
  }
  //获取前置变量限定符${的位置
  int start = text.indexOf(openToken, 0);
  if (start == -1) {
    return text;
  }
  //将text字符串转成字节数组
  char[] src = text.toCharArray();
  //开始位
  int offset = 0;
  final StringBuilder builder = new StringBuilder();
  StringBuilder expression = null;
  while (start > -1) { //如果前置变量限定符${存在
    //如果前置位置限定符${的前一位为\
    if (start > 0 && src[start - 1] == '\\') {
      // this open token is escaped. remove the backslash and continue.
      //连接\跟前置位置限定符${
      builder.append(src, offset, start - offset - 1).append(openToken);
      //将开始位变为前置位置限定符${后一位
      offset = start + openToken.length();
    } else { //如果前置位置限定符${的前一位不为\
      // found open token. let's search close token.
      if (expression == null) {
        expression = new StringBuilder();
      } else {
        expression.setLength(0);
      }
      //获取前置位置限定符${之前的所有字符
      builder.append(src, offset, start - offset);
      //开始位变为前置位置限定符${后一位
      offset = start + openToken.length();
      //从前置位置限定符${后一位开始获取后置位置限定符}的位置
      int end = text.indexOf(closeToken, offset);
      while (end > -1) { //后置位置限定符}存在
        //如果后置位置限定符}的前一位为\
        if (end > offset && src[end - 1] == '\\') {
          // this close token is escaped. remove the backslash and continue.
          //连接中间段的字符和后置位置限定符}
          expression.append(src, offset, end - offset - 1).append(closeToken);
          //将开始位变为后置位置限定符}的后一位
          offset = end + closeToken.length();
          //从新的开始位开始获取后置位置限定符}的位置
          end = text.indexOf(closeToken, offset);
        } else { //如果后置位置限定符}的前一位不为\
          //获取中间字符
          expression.append(src, offset, end - offset);
          //将开始位变为后置位置限定符}的后一位
          offset = end + closeToken.length();
          break;
        }
      }
      if (end == -1) { //如果后置位置限定符不存在
        // close token was not found.
        builder.append(src, start, src.length - start);
        offset = src.length;
      } else {
        builder.append(handler.handleToken(expression.toString()));
        offset = end + closeToken.length();
      }
    }
    start = text.indexOf(openToken, offset);
  }
  if (offset < src.length) {
    builder.append(src, offset, src.length - offset);
  }
  return builder.toString();
}

在XMLLanguageDriver中

代码语言:javascript
复制
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
  XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
  return builder.parseScriptNode();
}

在XMLScriptBuilder中

代码语言:javascript
复制
private final XNode context; //待解析的节点
代码语言:javascript
复制
//不同节点类型的节点处理器映射,对应的节点名称在初始化中被加载,节点名称有trim,where,when,set,foreach,if,choose,otherwise,bind
private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<String, NodeHandler>();
代码语言:javascript
复制
public SqlSource parseScriptNode() {
  //获取待解析的节点的混合子节点集合
  MixedSqlNode rootSqlNode = parseDynamicTags(context);
  SqlSource sqlSource = null;
  //根据是否是动态SQL,创建相应的SqlSource对象,SqlSource会在后面重点讲解
  if (isDynamic) {
    sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
  } else {
    sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
  }
  return sqlSource;
}
代码语言:javascript
复制
protected MixedSqlNode parseDynamicTags(XNode node) {
  //用于记录生成的sqlNode集合
  List<SqlNode> contents = new ArrayList<SqlNode>();
  //获取节点<selectkey>的所有子节点
  NodeList children = node.getNode().getChildNodes();
  //遍历所有子节点
  for (int i = 0; i < children.getLength(); i++) {
    //获取每一个子节点对象
    XNode child = node.newXNode(children.item(i));
    //如果该子节点的节点类型为文本节点
    if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
      //创建一个""字符串
      String data = child.getStringBody("");
      //创建一个空字符串的文本节点对象
      TextSqlNode textSqlNode = new TextSqlNode(data);
      //如果该文本节点是动态的(解析SQL语句,如果含有未解析的${}占位符,则为动态SQL)
      if (textSqlNode.isDynamic()) {
        //记录该动态节点
        contents.add(textSqlNode);
        isDynamic = true;
      } else { //如果不是动态节点,记录为静态文本节点
        contents.add(new StaticTextSqlNode(data));
      }
    } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
      //如果子节点是一个标签,那么一定是动态SQL,并且根据不同的动态标签生成不同的节点处理器
      String nodeName = child.getNode().getNodeName();
      NodeHandler handler = nodeHandlerMap.get(nodeName);
      if (handler == null) {
        throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
      }
      //处理动态SQL,并将解析得到的SQL节点对象放入记录集合
      handler.handleNode(child, contents);
      isDynamic = true;
    }
  }
  //返回记录集合的封装
  return new MixedSqlNode(contents);
}
代码语言:javascript
复制
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
  //获取处理节点的name属性
  final String name = nodeToHandle.getStringAttribute("name");
  //获取处理节点的value属性
  final String expression = nodeToHandle.getStringAttribute("value");
  //生成<bind>节点类型的Sql节点对象
  final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
  //将该节点对象添加到集合中
  targetContents.add(node);
}

SqlSource是一个接口,表示映射文件或注解中定义的SQL语句,但它表示的SQL语句可能含有动态SQL语句相关的节点或是占位符等需要解析的元素。

代码语言:javascript
复制
public interface SqlSource {
  //根据映射文件或注解描述的SQL语句,以及传入的参数,返回可执行的SQL
  BoundSql getBoundSql(Object parameterObject);

}

SqlSourceBuilder也是BaseBuilder的子类之一,在经过SqlNode.apply()方法的解析之后,SQL语句会被传递到SqlSourceBuilder中进行进一步的解析。它的核心parse()方法如下

代码语言:javascript
复制
//参数originalSql是经过SqlNode.apply()方法处理之后的SQL语句
//参数parameterType是用户传入的实参类型
//参数additionalParameters是经过SqlNode.apply()方法处理后的DynamicContext.bindings集合
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
  //创建ParameterMappingTokenHandler对象,它是解析#{}占位符中的参数属性以及替换占位符的核心
  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  //使用GenericTokenParser与ParameterMappingTokenHandler配合解析#{}占位符
  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  String sql = parser.parse(originalSql);
  //创建StaticSqlSource,其中封装了占位符被替换成"?"的SQL语句以及参数对应的ParameterMapping集合
  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

ParameterMappingTokenHandler是其内部类,也继承了SqlSourceBuilder,实现了TokenHandler接口,各字段含义如下

代码语言:javascript
复制
//用于记录解析得到的ParameterMapping集合
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
//参数类型
private Class<?> parameterType;
//DynamicContext.bindings集合对应的MeataObject对象
private MetaObject metaParameters;

它的核心方法为handleToken()

代码语言:javascript
复制
@Override
public String handleToken(String content) {
  parameterMappings.add(buildParameterMapping(content));
  return "?";
}
代码语言:javascript
复制
private ParameterMapping buildParameterMapping(String content) {
  //解析content字符串,并将解析结果存入Map中。例如#{__frc_item_0, javaType=int, jdbcType=NUMERIC, typeHandler=MyTypeHandler}被解析成
  //{"property" -> "__frch_item_0", "javaType" -> "int", "jdbcType" -> "NUMERIC", "typeHandler" -> "MyTypeHandler"}
  Map<String, String> propertiesMap = parseParameterMapping(content);
  //获取参数名称比如__frch_item_0
  String property = propertiesMap.get("property");
  //定义property的类实例
  Class<?> propertyType;
  //确定参数(__frch_item_0)的javaType属性
  //如果传入参数的反射属性中有property的getter方法
  if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
    //从metaObject反射工具中获取property属性对应的类实例
    propertyType = metaParameters.getGetterType(property);
    //如果类型处理注册器中有该参数类型的注册
  } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
    //将参数类型类实例直接赋给propertyType
    propertyType = parameterType;
    //如果从map中获取jdbcType的value为JdbcType枚举中的CURSOR
  } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
    //propertyType为ResultSet的类实例
    propertyType = java.sql.ResultSet.class;
    //如果参数类型是一个Map类型的实现类或者参数名为null
  } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
    //propertyType为Object的类实例
    propertyType = Object.class;
  } else {
    //以上都不是,获取参数类型的反射类MetaClass
    MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
    //如果property为该参数类型的一个getter属性
    if (metaClass.hasGetter(property)) {
      //获取该propety的类实例
      propertyType = metaClass.getGetterType(property);
    } else {
      //如果不是一个getter属性,则把propertyType仅定为一个Object的类实例
      propertyType = Object.class;
    }
  }
  //创建ParameterMapping的构建器,并设置ParameterMapping的相关配置
  ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
  Class<?> javaType = propertyType;
  String typeHandlerAlias = null;
  //遍历propertiesMap的所有键值对映射
  for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
    //获取每一个映射的key
    String name = entry.getKey();
    //获取每一个映射的value
    String value = entry.getValue();
    //如果key为javaType
    if ("javaType".equals(name)) {
      //通过类型别名映射解析value的别名
      javaType = resolveClass(value);
      //将解析后的别名放入ParameterMapping的javaType属性中
      builder.javaType(javaType);
    //如果key为jdbcType
    } else if ("jdbcType".equals(name)) {
      //通过jdbc别名映射解析value的枚举,放入ParameterMapping的jdbcType属性中
      builder.jdbcType(resolveJdbcType(value));
    //如果key为mode
    } else if ("mode".equals(name)) {
      //通过mode别名映射解析value的枚举(此处为进出参数),放入ParameterMapping的mode属性中
      builder.mode(resolveParameterMode(value));
    //如果key为numericScale
    } else if ("numericScale".equals(name)) {
      //将vlue转化为整形,放入ParameterMapping的numericScale属性中
      builder.numericScale(Integer.valueOf(value));
    //如果key为resultMap
    } else if ("resultMap".equals(name)) {
      //将value放入ParameterMapping的resultMapId属性中
      builder.resultMapId(value);
    //如果key为typeHandler
    } else if ("typeHandler".equals(name)) {
      //获取value赋值typeHandlerAlias
      typeHandlerAlias = value;
    //如果key为jdbcTypeName
    } else if ("jdbcTypeName".equals(name)) {
      //将value放入ParameterMapping的jdbcTypeName属性
      builder.jdbcTypeName(value);
    } else if ("property".equals(name)) {
      // Do Nothing
    } else if ("expression".equals(name)) {
      throw new BuilderException("Expression based parameters are not supported yet");
    } else {
      throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + parameterProperties);
    }
  }
  //如果typeHandlerAlias不为null
  if (typeHandlerAlias != null) {
    //获取TypeHandler对象并放入ParameterMapping的typeHandler属性中
    builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
  }
  //创建ParameterMapping对象,如果没有指定类型处理器,则会在build()方法中,根据java类型,数据库字段类型从类型处理器注册器中获取对应的类型处理器对象
  return builder.build();
}
代码语言:javascript
复制
private Map<String, String> parseParameterMapping(String content) {
  try {
    //解析content字符串,并存入ParameterExpression实例中
    return new ParameterExpression(content);
  } catch (BuilderException ex) {
    throw ex;
  } catch (Exception ex) {
    throw new BuilderException("Parsing error was found in mapping #{" + content + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
  }
}

ParameterMapping中记录了#{}占位符中的参数属性,其各个字段的含义如下

代码语言:javascript
复制
private Configuration configuration; //全局配置信息

private String property; //传入进来的参数name
private ParameterMode mode; //输入参数还是输出参数,枚举类型
private Class<?> javaType = Object.class; //参数的Java类型
private JdbcType jdbcType; //参数的JDBC类型
private Integer numericScale; //浮点参数的精度
private TypeHandler<?> typeHandler; //参数对应的类型处理器对象
private String resultMapId; //参数对应的ResultMap的Id
private String jdbcTypeName; //参数的jdbcTypeName属性
private String expression; 
代码语言:javascript
复制
public ParameterMapping build() {
  //解析parameterMapping参数类型处理器
  resolveTypeHandler();
  //对参数类型处理器检查有效性
  validate();
  return parameterMapping;
}
代码语言:javascript
复制
private void resolveTypeHandler() {
  //如果类型处理器对象为null且Java类实例不为null
  if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
    //获取全局配置信息
    Configuration configuration = parameterMapping.configuration;
    //获取全局配置信息的类型处理器注册器
    TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    //获取java类型对应的数据库字段类型的类型处理器对象
    parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
  }
}
代码语言:javascript
复制
private void validate() {
  //如果参数java类型为ResultSet
  if (ResultSet.class.equals(parameterMapping.javaType)) {
    //如果参数对应的resultMapId为null,抛出异常
    if (parameterMapping.resultMapId == null) { 
      throw new IllegalStateException("Missing resultmap in property '"  
          + parameterMapping.property + "'.  " 
          + "Parameters of type java.sql.ResultSet require a resultmap.");
    }
  //如果参数java类型不为ResultSet            
  } else {
    //如果参数的类型处理器为null,抛出异常
    if (parameterMapping.typeHandler == null) { 
      throw new IllegalStateException("Type handler was null on parameter mapping for property '"
        + parameterMapping.property + "'. It was either not specified and/or could not be found for the javaType ("
        + parameterMapping.javaType.getName() + ") : jdbcType (" + parameterMapping.jdbcType + ") combination.");
    }
  }
}

在ParameterExpression中,ParameterExpression继承于HashMap

代码语言:javascript
复制
public ParameterExpression(String expression) {
  //解析expression字符串
  parse(expression);
}
代码语言:javascript
复制
private void parse(String expression) {
  //获取expression中大于空格字符的索引
  int p = skipWS(expression, 0);
  //如果该索引的字符为'('
  if (expression.charAt(p) == '(') {
    //处理该字符串中的内容
    expression(expression, p + 1);
  } else { //如果该索引的字符不为'('
    //以",:"为为边界,将p到",:"索引之间的字符串为value,"property"为key存入当前ParameterExpression实例
    //再处理后续jdbcTypeOpt()方法
    property(expression, p);
  }
}
代码语言:javascript
复制
private int skipWS(String expression, int p) {
  for (int i = p; i < expression.length(); i++) {
    //如果expression的每一个字符为大于空格以上的字符,返回该字符的索引
    if (expression.charAt(i) > 0x20) {
      return i;
    }
  }
  //如果没有一个大于空格以上的字符,返回该字符串的长度
  return expression.length();
}
代码语言:javascript
复制
private void expression(String expression, int left) {
  int match = 1;
  int right = left + 1;
  //获取字符串中(和)中的索引位置
  while (match > 0) {
    //如果expression中'('的后两位字符为')',match递减
    if (expression.charAt(right) == ')') {
      match--;
      //如果expression中'('的后两位字符为'(',match递增
    } else if (expression.charAt(right) == '(') {
      match++;
    }
    //right递增
    right++;
  }
  //将expression和()中间的字符串以key,value存入当前ParameterExpression实例
  put("expression", expression.substring(left, right - 1));
  //将':'后面的字符串,以','为分隔符为value,将"jdbcType"为key存入当前ParameterExpression实例
  //再将'='两边的字符串,以','为分隔符,以key,value形式存入当前ParameterExpression实例
  jdbcTypeOpt(expression, right);
}
代码语言:javascript
复制
private void jdbcTypeOpt(String expression, int p) {
  //获取p索引之后的大于空格的字符索引
  p = skipWS(expression, p);
  //如果expression之中存在该字符
  if (p < expression.length()) {
    //且该索引的字符为':'
    if (expression.charAt(p) == ':') {
      //将':'后面的字符串,以','为分隔符为value,将"jdbcType"为key存入当前ParameterExpression实例
      //再将'='两边的字符串,以','为分隔符,以key,value形式存入当前ParameterExpression实例
      jdbcType(expression, p + 1);
      //且该索引的字符为','
    } else if (expression.charAt(p) == ',') {
      //将'='两边的字符串,以','为分隔符,以key,value形式存入当前ParameterExpression实例
      option(expression, p + 1);
    } else {
      throw new BuilderException("Parsing error in {" + expression + "} in position " + p);
    }
  }
}
代码语言:javascript
复制
private void jdbcType(String expression, int p) {
  //获取p索引之后的大于空格的字符索引
  int left = skipWS(expression, p);
  //获取','在expression的位置
  int right = skipUntil(expression, left, ",");
  if (right > left) {
    //将jdbcType以及去除了left,rignt两边空格的字符串以key,value存入当前ParameterExpression实例
    put("jdbcType", trimmedStr(expression, left, right));
  } else {
    throw new BuilderException("Parsing error in {" + expression + "} in position " + p);
  }
  //将'='两边的字符串,以','为分隔符,以key,value形式存入当前ParameterExpression实例
  option(expression, right + 1);
}
代码语言:javascript
复制
private int skipUntil(String expression, int p, final String endChars) {
  //从p位置开始,遍历后续的字符串
  for (int i = p; i < expression.length(); i++) {
    //遍历每一个字符
    char c = expression.charAt(i);
    //如果该字符在endChars内,返回该字符的索引位
    if (endChars.indexOf(c) > -1) {
      return i;
    }
  }
  //否则返回expression的长度
  return expression.length();
}
代码语言:javascript
复制
private String trimmedStr(String str, int start, int end) {
  while (str.charAt(start) <= 0x20) {
    start++;
  }
  while (str.charAt(end - 1) <= 0x20) {
    end--;
  }
  //去掉str两边的空格
  return start >= end ? "" : str.substring(start, end);
}
代码语言:javascript
复制
private void option(String expression, int p) {
  //获取p索引之后的大于空格的字符索引
  int left = skipWS(expression, p);
  //如果expression之中存在该字符
  if (left < expression.length()) {
    //获取'='在expression的位置(left之后)
    int right = skipUntil(expression, left, "=");
    //获取去除了left,rignt两边空格的字符串
    String name = trimmedStr(expression, left, right);
    left = right + 1;
    //获取','在expression的位置(left之后)
    right = skipUntil(expression, left, ",");
    //获取去除了left,rignt两边空格的字符串
    String value = trimmedStr(expression, left, right);
    //将name,value存入当前ParameterExpression实例
    put(name, value);
    //递归后面所有的字符串
    option(expression, right + 1);
  }
}
代码语言:javascript
复制
private void property(String expression, int left) {
  //如果该字符在expression中存在
  if (left < expression.length()) {
    //获取',:'在expression的位置(left之后)
    int right = skipUntil(expression, left, ",:");
    //以"property"为key;left,right中间去除两边空格的字符串为value,存入当前ParameterExpression实例
    put("property", trimmedStr(expression, left, right));
    //将':'后面的字符串,以','为分隔符为value,将"jdbcType"为key存入当前ParameterExpression实例
    //再将'='两边的字符串,以','为分隔符,以key,value形式存入当前ParameterExpression实例
    jdbcTypeOpt(expression, right);
  }
}

在TypeHandlerRegistry中

代码语言:javascript
复制
//记录java类型向jdbcType转换,需要使用到TypeHandler
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
代码语言:javascript
复制
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class
代码语言:javascript
复制
public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
  return getTypeHandler((Type) type, jdbcType);
}
代码语言:javascript
复制
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
  if (ParamMap.class.equals(type)) {
    return null;
  }
  //获取type对应的数据库字段类型与类型处理器的映射
  Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
  TypeHandler<?> handler = null;
  //如果该映射不为null
  if (jdbcHandlerMap != null) {
    //获取该映射中数据库字段类型对应的类型处理器对象
    handler = jdbcHandlerMap.get(jdbcType);
    if (handler == null) {
      //如果该处理器对象为null,直接获取该映射中null对应的处理器对象
      handler = jdbcHandlerMap.get(null);
    }
    if (handler == null) {
      //如果处理器对象依然为null,从该映射中找到第一个不为null的处理器对象
      handler = pickSoleHandler(jdbcHandlerMap);
    }
  }
  // type drives generics here
  return (TypeHandler<T>) handler;
}
代码语言:javascript
复制
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
  //从TYPE_HANDLER_MAP中获取type接口对应的数据库类型与类型处理器的映射
  Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
  if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {
    return null;
  }
  //如果该映射为null且type为Class类型(Class类为Type接口的实现类)
  if (jdbcHandlerMap == null && type instanceof Class) {
    //将type强制转换成Class实例
    Class<?> clazz = (Class<?>) type;
    //如果该类为枚举类型
    if (clazz.isEnum()) {
      //以枚举和以该枚举来构造的类型处理器对象的映射
      jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
      //如果该映射为null
      if (jdbcHandlerMap == null) {
        //将clazz类实例以及以clazz(枚举类型)为构造参数构造的defaultEnumTypeHandler对象为参数,注册进TYPE_HANDLER_MAP中
        register(clazz, getInstance(clazz, defaultEnumTypeHandler));
        //返回在TYPE_HANDLER_MAP中由clazz获取的jdbcType和TypeHandler的映射
        return TYPE_HANDLER_MAP.get(clazz);
      }
    //如果该类不为枚举类型
    } else {
      //追溯clazz的所有父类可能对应的数据库字段类型与类型处理器的映射
      jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
    }
  }
  //将查到的映射为value,type为key放入TYPE_HANDLER_MAP中
  TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
  //返回查找到的映射
  return jdbcHandlerMap;
}
代码语言:javascript
复制
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
  //遍历clazz类实例的所有接口
  for (Class<?> iface : clazz.getInterfaces()) {
    //获取每一个接口对应的数据库类型与类型处理器的映射
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface);
    //如果该映射为null
    if (jdbcHandlerMap == null) {
      //递归遍历该接口的所有父接口
      jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
    }
    //如果该映射不为null
    if (jdbcHandlerMap != null) {
      // Found a type handler regsiterd to a super interface
      HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<JdbcType, TypeHandler<?>>();
      //遍历该映射
      for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
        //创建一个以枚举类型为构造参数的类型处理器实例对象的value,映射的key为key,放入新的映射中
        newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
      }
      return newMap;
    }
  }
  return null;
}
代码语言:javascript
复制
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
  //获取clazz的父类
  Class<?> superclass =  clazz.getSuperclass();
  //如果父类为null,或者父类为Object,返回null
  if (superclass == null || Object.class.equals(superclass)) {
    return null;
  }
  //从TYPE_HANDLER_MAP中查找父类对应的数据库字段类型与类型处理器的映射
  Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
  if (jdbcHandlerMap != null) {
    //如果该映射不为null,返回该映射
    return jdbcHandlerMap;
  } else {
    //如果该映射为null,递归查找该父类的父类对应的数据库字段类型与类型处理器的映射
    return getJdbcHandlerMapForSuperclass(superclass);
  }
}
代码语言:javascript
复制
private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
  TypeHandler<?> soleHandler = null;
  //遍历jdbcHandlerMap的所有value
  for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
    if (soleHandler == null) {
      //如果soleHandler为null,将每一个value赋给soleHandler
      soleHandler = handler;
    //如果soleHandler不为null且value与soleHandler不是同一个Class,返回null
    } else if (!handler.getClass().equals(soleHandler.getClass())) {
      // More than one type handlers registered.
      return null;
    }
  }
  //返回找到的第一个不为null的value
  return soleHandler;
}

经过SqlSourceBuilder解析后,SqlNode解析后的SQL语句的变化为(示例)

select * from XXX where id in ( ? , ? ),其中(?,?)为一个parameterMappings集合,第一个?代表一个ParameterMapping,其值类似为{property='__frch_item_0',mode=IN,javaType=class java.lang.Integer},第二个?代表一个ParameterMapping,其值类似为{property='__frch_item_1',mode=IN,javaType=class java.lang.Integer}

之后,SqlSourceBuilder会将上述SQL语句以及parameterMappings集合封装成StaticSqlSource对象。StaticSqlSource是SqlSource接口的一个实现类,它的接口方法如下

代码语言:javascript
复制
@Override
public BoundSql getBoundSql(Object parameterObject) {
  return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}

BoundSql中核心字段含义如下

代码语言:javascript
复制
private final String sql; //该字段中记录了SQL语句,该SQL语句中可能含有"?"占位符
private final List<ParameterMapping> parameterMappings; //SQL中的参数属性集合,ParameterMapping的集合
private final Object parameterObject; //客户端执行SQL时传入的实际参数
//空的HashMap集合,之后会复制DynamicContext.bindings集合中的内容
private final Map<String, Object> additionalParameters;
//additionalParameters集合对应的MetaObject对象
private final MetaObject metaParameters;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档