Mybatis3源码笔记(四)Configuration(续)

1.pluginElement(root.evalNode("plugins")) 解析plugins节点(注册interceptorChain里记录对应的拦截器)
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //解析interceptor对应的class名
        String interceptor = child.getStringAttribute("interceptor");
       //解析properties 
        Properties properties = child.getChildrenAsProperties();
       //根据interceptor从resolveClass里拿出class,通过反射生成实例
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
       //给interceptorInstance设置properties 
        interceptorInstance.setProperties(properties);
       //注册到interceptorChain里,责任链模式
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
  <plugins>
    <plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">
      <property name="pluginProperty" value="100"/>
    </plugin>
  </plugins>
public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}
2.objectFactoryElement,objectWrapperFactoryElement,reflectorFactoryElement 这三个节点的解析都是类似反射实例化操作,就不解释了。
  private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties properties = context.getChildrenAsProperties();
      ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(properties);
      configuration.setObjectFactory(factory);
    }
  }

  private void objectWrapperFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      configuration.setObjectWrapperFactory(factory);
    }
  }

  private void reflectorFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      configuration.setReflectorFactory(factory);
    }
  }
3.settingsElement(settings) 设置其它的setting参数。
  private void settingsElement(Properties props) {
    //指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    //指定发现自动映射目标未知列(或者未知属性类型)的行为
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    //是否启用二级缓存
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    //指定具有懒加载能力的对象所用到的代理工厂
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
   //是否启用懒加载
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    //当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    //是否允许单一语句返回多结果集
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    //使用列标签代替列名
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    //设置主键自增
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    //设置默认的ExecutorType
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    //设置默认的Statement执行超时时间
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    //设置默认获取数量
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    //设置默认的ResultSet类型
    configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
    //是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    //设置允许在嵌套语句中使用分页(RowBounds)
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    //MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
    // 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    //当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    //指定对象的哪个方法触发一次延迟加载
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    //设置ResultHandler的安全性检查
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    //设置默认的ScriptingLanguage,用来生成boundsql
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    //设置默认的defaultEnumTypeHandler
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    //指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    // 允许使用方法签名中的名称作为语句参数名称。
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    //当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    //设置log前缀
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    //设置ConfigurationFactory
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    //设置shrinkWhitespacesInSql,删除sql中多余的空白字符
    configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
    //设置defaultSqlProviderType,动态sql拼接
    configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
  }
  <settings>
    <setting name="autoMappingBehavior" value="NONE"/>
    <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
    <setting name="cacheEnabled" value="false"/>
    <setting name="proxyFactory" value="CGLIB"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="true"/>
    <setting name="multipleResultSetsEnabled" value="false"/>
    <setting name="useColumnLabel" value="false"/>
    <setting name="useGeneratedKeys" value="true"/>
    <setting name="defaultExecutorType" value="BATCH"/>
    <setting name="defaultStatementTimeout" value="10"/>
    <setting name="defaultFetchSize" value="100"/>
    <setting name="defaultResultSetType" value="SCROLL_INSENSITIVE"/>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <setting name="safeRowBoundsEnabled" value="true"/>
    <setting name="localCacheScope" value="STATEMENT"/>
    <setting name="jdbcTypeForNull" value="${jdbcTypeForNull}"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString,xxx"/>
    <setting name="safeResultHandlerEnabled" value="false"/>
    <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.defaults.RawLanguageDriver"/>
    <setting name="callSettersOnNulls" value="true"/>
    <setting name="logPrefix" value="mybatis_"/>
    <setting name="logImpl" value="SLF4J"/>
    <setting name="vfsImpl" value="org.apache.ibatis.io.JBoss6VFS"/>
    <setting name="configurationFactory" value="java.lang.String"/>
    <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
    <setting name="shrinkWhitespacesInSql" value="true"/>
    <setting name="defaultSqlProviderType" value="org.apache.ibatis.builder.XmlConfigBuilderTest$MySqlProvider"/>
  </settings>
4.environmentsElement(root.evalNode("environments")) 解析environments
  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      //environment没设置的话,默认用defalut对应的value
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        //根据environment取对应的配置
        if (isSpecifiedEnvironment(id)) {
          //实例化TransactionFactory
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //实例化DataSourceFactory
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);
          //设置environment
          configuration.setEnvironment(environmentBuilder.build());
          break;
        }
      }
    }
  }
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC">
        <property name="" value=""/>
      </transactionManager>
      <dataSource type="UNPOOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
5.databaseIdProviderElement(root.evalNode("databaseIdProvider"))解析databaseIdProvider节点(databaseId)
  private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";
      }
      Properties properties = context.getChildrenAsProperties();
      //实例化databaseIdProvider
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      //根据environment中设置的dataSource取得databaseId
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      //设置databaseId
      configuration.setDatabaseId(databaseId);
    }
  }
  <databaseIdProvider type="DB_VENDOR">
    <property name="Apache Derby" value="derby"/>
  </databaseIdProvider>
6.typeHandlerElement(root.evalNode("typeHandlers"))解析typeHandlers节点,注册类型转换器(typeHandlerRegistry)
  private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //package配置,老规矩直接取该路径下的所有TypeHandler类型的class注册到typeHandlerRegistry
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          //根据下图xml中配置的三种不同的配置方式,注册到typeHandlerRegistry
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }
  <typeHandlers>
    <typeHandler javaType="String" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
    <typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
    <typeHandler handler="org.apache.ibatis.builder.CustomLongTypeHandler"/>
    <package name="org.apache.ibatis.builder.typehandler"/>
  </typeHandlers>
看下只有typeHandlerClass的注册方式,其它两种都是大同小异:
  public void register(Class<?> typeHandlerClass) {
    //flag
    boolean mappedTypeFound = false;
    //取MappedTypes注解
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
      for (Class<?> javaTypeClass : mappedTypes.value()) {
        //根据MappedTypes的value值当作javaTypeClass,走javaTypeClass+typeHandlerClass的注册
        register(javaTypeClass, typeHandlerClass);
        mappedTypeFound = true;
      }
    }
    //如果没有MappedTypes注解,走只有typeHandlerClass的注册
    if (!mappedTypeFound) {
      register(getInstance(null, typeHandlerClass));
    }
  }
  public <T> void register(TypeHandler<T> typeHandler) {
     ...
    // (从3.1.0开始,尝试走自动发现的注册)
    if (!mappedTypeFound && typeHandler instanceof TypeReference) {
      try {
        TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
        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) {
      register((Class<T>) null, typeHandler);
    }
  }
  Type getSuperclassTypeParameter(Class<?> clazz) {
    //嵌套查询反射获得带有泛型的父类
    Type genericSuperclass = clazz.getGenericSuperclass() ;
    if (genericSuperclass instanceof Class) {
      // try to climb up the hierarchy until meet something useful
      if (TypeReference.class != genericSuperclass) {
        return getSuperclassTypeParameter(clazz.getSuperclass());
      }
      throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
        + "Remove the extension or add a type parameter to it.");
    }
    //取出TypeReference<T>中的泛型的第一个参数,例如 BaseTypeHandler<BigDecimal>中的BigDecimal
    Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
    // TODO remove this when Reflector is fixed to return Types
    if (rawType instanceof ParameterizedType) {
      rawType = ((ParameterizedType) rawType).getRawType();
    }
    return rawType;
  }
  private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
      if (map == null || map == NULL_TYPE_HANDLER_MAP) {
        map = new HashMap<>();
      }
      map.put(jdbcType, handler);
      typeHandlerMap.put(javaType, map);
    }
    allTypeHandlersMap.put(handler.getClass(), handler);
  }
另外TypeHandlerRegistry在初始化的时候已经内置了一些共通的TypeHandler:
  public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());
    ...

posted @ 2021-03-23 13:51  可飞  阅读(94)  评论(0编辑  收藏  举报