2.configuration解析

1. 配置文件初始化

mybatis 中包含了很多的配置项,具体每一项的讲解 官网 也很详细,对配置项解析后由Configuration 类维护

  • configuration(配置)
  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境配置)
    • environment(环境变量)
    • transactionManager(事务管理器)
    • dataSource(数据源)
  • mappers(映射器)

1.1 Java API 初始化

PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT");
dataSource.setUsername("root");
dataSource.setPassword("root");
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(UserMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

1.2 XML 配置初始化

相交于 Java API 的方式,XML 配置初始化,必然会多出 XML 的解析部分。代码如下:

String resource = "com/scarecrow/mybatis/config/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
Properties properties = new Properties();
properties.setProperty("password", "scarecrow");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "development1", properties);

下面是一个相对完整的配置示例:

<?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">

<!-- ELEMENT 标签有顺序 -->
<configuration>
  <!-- properties resource 指定配置文件-->
  <properties resource="com/scarecrow/mybatis/properties/mybatis.properties">
    <!-- 启用默认值特性 -->
    <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    <!-- 修改默认值的分隔符 -->
    <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis-test?serverTimezone = GMT"/>
    <property name="password" value="scarecrow"/>
  </properties>
  <settings>
    <setting name="cacheEnabled" value="true"/>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
  </settings>
  <!-- 类型的别名,简化全路径类名的拼写 xml中的type 我们可以不用写全路径类名。可以使用别名或者直接使用类名即可-->
  <!-- TypeAliasRegistry 系统预先定义好的类型别名-->
  <typeAliases>
    <!-- package 与 typeAlias 不可共用-->
    <!-- package 报下的类名也可使用@Alias注解指定别名,不使用类名 -->
    <package name="com.scarecrow.mybatis.po"/>
    <!-- xml中使用 alias 指定的别名即可-->
    <!--<typeAlias type="com.scarecrow.mybatis.po.UserPO" alias="userPo"/>-->
  </typeAliases>
  <typeHandlers>
    <typeHandler handler="org.apache.ibatis.type.DateTypeHandler" jdbcType="DATE" javaType="java.util.Date"/>
    <typeHandler handler="com.scarecrow.mybatis.type.CustomBooleanTypeHandler"/>
  </typeHandlers>
  <!-- 数据库返回的结果集转换为实体类 -->
  <objectFactory type="com.scarecrow.mybatis.objectfactory.MyCustomObjectFactory">
    <property name="scarecrow" value="scarecrow"/>
    <property name="scarecrow1" value="scarecrow1"/>
  </objectFactory>
  <plugins>
    <plugin interceptor="com.scarecrow.mybatis.plugins.MyCustomPlugin">
      <property name="selectPlugin" value="selectPlugin"/>
    </plugin>
  </plugins>
  <environments default="development1">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <!-- properties 中的配置项 -->
        <property name="driver" value="${driver}"/>
        <!-- properties文件中的配置 -->
        <property name="url" value="${url1}"/>
        <!-- 如果属性 username 没有被配置,username 属性的值将为 root -->
        <!-- 这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性 enable-default-value 来开启这个特性 -->
        <!-- 默认分隔符: default-value-separator 来修改默认分隔符为?:-->
        <property name="username" value="${username?:root}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
    <environment id="development1">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username?:root}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>

  <!-- 数据库厂商标示 -->
  <!-- name属性是数据库名称,value是我们自定义的别名-->
  <databaseIdProvider type="VENDOR">
    <property name="DB2" value="db2"/>
    <property name="Oracle" value="oracle" />
    <property name="MySQL" value="mysql"/>
  </databaseIdProvider>

  <mappers>
    <!-- 三者使用其中一个即可-->
     <mapper resource="com/scarecrow/mybatis/mapper/UserMapper.xml"/>
    <!-- <mapper class="com.scarecrow.mybatis.mapper.UserMapper"/>-->
     <!--<package name="com.scarecrow.mybatis.mapper"/>-->
  </mappers>

</configuration>

2. Configuration详解

image-20210428232947699

Configuration类保存了所有Mybatis的配置信息。一般情况下Mybatis在运行过程中只会创建一个Configration对象,并且配置信息不能再被修改。

configuration的属性主要分为两大部分:

  • 从mybatis-config.xml中读取的配置

    // 环境
    protected Environment environment;
    // ---------以下都是<settings>节点-------
    protected boolean safeRowBoundsEnabled;
    protected boolean safeResultHandlerEnabled = true;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled = true;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel = true;
    // 默认启用缓存
    protected boolean cacheEnabled = true;
    protected boolean callSettersOnNulls;
    protected boolean useActualParamName = true;
    protected boolean returnInstanceForEmptyRow;
    protected boolean shrinkWhitespacesInSql;
    
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected Class<?> defaultSqlProviderType;
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
    protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
    protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
    protected Integer defaultStatementTimeout;
    protected Integer defaultFetchSize;
    protected ResultSetType defaultResultSetType;
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    
    protected Properties variables = new Properties();
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    
    protected boolean lazyLoadingEnabled = false;
    protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
    
    protected String databaseId;
    
    // mybatis 插件
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    // 类型处理器
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
    // 类型别名
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    // mapper代理对象接口
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
  • 从mapper配置文件或Mapper注解读取的配置

    // 解析后的SQL语句
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
        .conflictMessageProducer((savedValue, targetValue) ->
            ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
    protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
    
    // 存储解析后的resultMap
    protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
    protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
    // 存储加载后的 sql 片段,在解析SQL节点时会用到
    protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
    

Configuration的无参的构造方法,默认注册了很多类型别名。我们只需这些别名就可以知道具体的类型

public Configuration() {
  typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

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

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

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

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

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

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

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

3. 解析流程

image-20210428232340105

3.1 SqlSessionFactoryBuilder

主要用途:解析配置文件configuration,及创建SqlSessionFactory 会话工厂

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

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

3.2 XMLConfigBuilder

主要解析mybatis-config 配置文件,与之相关的还有下面几个

  • XMLConfigBuilder:解析mybatis-config文件
  • XMLMapperBuilder:解析 Mapper 映射器
  • XMLStatementBuilder:解析增删改查标签

image-20210428235302557

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  // Configuration 对象在这里创建。配置文件的解析属性就学会放在 此对象中
  super(new Configuration());
  ErrorContext.instance().resource("SQL Mapper Configuration");
  // 创建SqlSessionFactory对象传进来的属性
  this.configuration.setVariables(props);
  this.parsed = false;
  // 创建SqlSessionFactory对象传进来的运行环境。与config文件的environments 配置节点有关
  this.environment = environment;
  this.parser = parser;
}

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
private void parseConfiguration(XNode root) {
  try {
    // issue #117 read properties first
    // 分部解析自定义的config文件
    // 1.properties(variables)
    propertiesElement(root.evalNode("properties"));
    // 2.settings
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    // 显式定义用什么log框架,不定义则用默认的自动发现jar包机制
    loadCustomLogImpl(settings);
    // 3.typeAliases 类型别名(TypeAliasRegistry)
    typeAliasesElement(root.evalNode("typeAliases"));
    // 4.插件(interceptorChain)
    pluginElement(root.evalNode("plugins"));
    // 5.对象工厂(objectFactory)
    objectFactoryElement(root.evalNode("objectFactory"));
    // 6.对象包装工厂
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    // 7.设置(settongs)
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    // 8.环境
    environmentsElement(root.evalNode("environments"));
    // 9.databaseIdProvider
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    // 10.类型处理器
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 11.映射器
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

4. 配置文件的详细解析

4.1 properties节点

private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    // 如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:
    // 1).在 properties 元素体内指定的属性首先被读取。
    // 2).从类路径resource下资源或 properties 元素的 url 属性中加载的属性第二被读取,它会覆盖已经存在的完全一样的属性。
    // 3).作为方法参数传递的属性最后被读取, 它也会覆盖任一已经存在的完全一样的属性,这些属性可能是从 properties 元素体内和资源/url 属性中加载的。

    // 1.XNode.getChildrenAsProperties函数方便得到孩子所有Properties
    Properties defaults = context.getChildrenAsProperties();
    // 2.然后查找resource或者url,加入前面的Properties
    String resource = context.getStringAttribute("resource");
    String url = context.getStringAttribute("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.");
    }
    if (resource != null) {
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    // 3.Variables也全部加入Properties
    Properties vars = configuration.getVariables();
    if (vars != null) {
      defaults.putAll(vars);
    }
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}

4.2 settings节点

private void settingsElement(Properties props) {
  // 如何自动映射列到字段/属性
  configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
  configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
  // 缓存
  configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
  // proxyFactory (CGLIB | JAVASSIST)
  // 延迟加载的核心技术就是用代理模式,CGLIB/JAVASSIST两者选一
  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));
  // 允许 JDBC 支持生成的键
  configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
  // 配置默认的执行器
  configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  // 超时时间
  configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));

  configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
  configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
  // 是否将DB字段自动映射到驼峰式Java属性(A_COLUMN-->aColumn)
  configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
  // 嵌套语句上使用RowBounds
  configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  // 默认用session级别的缓存
  configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
  // 为null值设置jdbctype
  configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
  // Object的哪些方法将触发延迟加载
  configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  //使用安全的ResultHandler
  configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
  //动态SQL生成语言所使用的脚本语言
  configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
  configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
  configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
  configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
  //logger名字的前缀
  configuration.setLogPrefix(props.getProperty("logPrefix"));
  configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
  configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
}

4.3 typeAliases节点

<typeAliases>
  <!-- package 与 typeAlias 不可共用-->
  <!-- package 报下的类名也可使用@Alias注解指定别名,不使用类名 -->
  <package name="com.scarecrow.mybatis.po"/>
  <!-- xml中使用 alias 指定的别名即可-->
  <!--<typeAlias type="com.scarecrow.mybatis.po.UserPO" alias="userPo"/>-->
</typeAliases>
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.registerAliases,去包下找所有类,然后注册别名(有@Alias注解则用,没有则取类的simpleName)
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } else {
        // 如果是typeAlias
        String alias = child.getStringAttribute("alias");
        String type = child.getStringAttribute("type");
        try {
          Class<?> clazz = Resources.classForName(type);
          // 根据Class名字来注册类型别名
          // 调用TypeAliasRegistry.registerAlias
          // alias可以省略
          if (alias == null) {
            typeAliasRegistry.registerAlias(clazz);
          } else {
            typeAliasRegistry.registerAlias(alias, clazz);
          }
        } catch (ClassNotFoundException e) {
          throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
        }
      }
    }
  }
}
4.3.1 TypeAliasRegistry

类型别名注册:

  1. 通过config xml配置的alias优先级最高

  2. @Alias注解的value值

  3. class.simpleName()

String key = alias.toLowerCase(Locale.ENGLISH);

register时会把别名全部转为小写字母当作key。

resolve时也会把别名全部转为小写字母当作key。

/**
 * @author Clinton Begin
 * 类型别名注册
 * 1、通过xml配置的alias优先级最高
 * 2、@Alias注解的value值
 * 3、class.simpleName()
 */
public class TypeAliasRegistry {

  // key是别名,value是 对象的class类型
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
    
  // 构造函数里注册系统内置的类型别名
  public TypeAliasRegistry() {
      // ...
  }  
  // 扫描并注册包下的所有类
  public void registerAliases(String packageName) {
    registerAliases(packageName, Object.class);
  }

  // 扫描并注册包下所有继承于superType的类型别
  public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // IsA是ResolverUtil的内部类
    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()) {
        registerAlias(type);
      }
    }
  }
    
  public void registerAlias(Class<?> type) {
    // 如果没有类型别名,用Class.getSimpleName来注册(类名而非全路径类名)
    String alias = type.getSimpleName();
    // 通过Alias注解来注册(Class.getAnnotation)
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    }
    registerAlias(alias, type);
  }
  // 注册类型别名
  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);
    // 如果已经存在key了,且value和之前不一致异常
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + 	       "'.");
    }
    typeAliases.put(key, value);
  }
    
  // 注册类型别名
  public void registerAlias(String alias, String value) {
    try {
      // 通过全路径类名
      registerAlias(alias, Resources.classForName(value));
    } catch (ClassNotFoundException e) {
      throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + e, e);
    }
  }
  
  // 解析类型别名
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      // 原理就很简单了,从HashMap里找对应的键值,找到则返回类型别名对应的Class
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        // 找不到,再试着将String直接转成Class(这样怪不得我们也可以直接用java.lang.Integer的方式定义,也可以就int这么定义)
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }  
}    

4.4 typeHandlers节点

<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.DateTypeHandler" jdbcType="DATE" javaType="java.util.Date"/>
  <typeHandler handler="com.scarecrow.mybatis.type.CustomBooleanTypeHandler"/>
</typeHandlers>
/**
 * 数据库存储字段为 bit
 * 1:是,0:否
 * 通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型
 * 1、在类型处理器的配置元素(typeHandler 元素)上增加一个 javaType 属性
 * 2、在类型处理器的类上增加一个 @MappedTypes 注解指定与其关联的 Java 类型列表。如果在 javaType 属性中也同时指定,则注解上的配置将被忽略
 *
 * 通过两种方式来指定关联的 JDBC 类型
 * 1、在类型处理器typeHandler的配置元素上增加一个 jdbcType 属性
 * 2、在类型处理器的类上增加一个 @MappedJdbcTypes 注解指定与其关联的 JDBC 类型列表。 如果在 jdbcType 属性中也同时指定,则注解上的配置将被忽略
 *
 *  当在 ResultMap 中决定使用哪种类型处理器时,此时 Java 类型是已知的(从结果类型中获得),但是 JDBC 类型是未知的。
 *  因此 Mybatis 使用 javaType=[Java 类型], jdbcType=null 的组合来选择一个类型处理器。详见UserMapper.resultMap
 *  如果希望能在 ResultMap 中隐式地使用类型处理器,那么设置 @MappedJdbcTypes 注解的 includeNullJdbcType=true 即可
 *
 *  自定义注册的TypeHandler比系统注册的使用优先级高
 */
@MappedJdbcTypes(value = JdbcType.BOOLEAN, includeNullJdbcType=true)
@MappedTypes(Boolean.class)
public class CustomBooleanTypeHandler extends BaseTypeHandler<Boolean> {
  // ...
}
private void typeHandlerElement(XNode parent) {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // 如果是package
      if ("package".equals(child.getName())) {
        String typeHandlerPackage = child.getStringAttribute("name");
        // 调用TypeHandlerRegistry.register,去包下找所有类
        typeHandlerRegistry.register(typeHandlerPackage);
      } else {
        // 如果是typeHandler
        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);
        // 调用TypeHandlerRegistry.register(以下是3种不同的参数形式)
        if (javaTypeClass != null) {
          if (jdbcType == null) {
            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
          } else {
            typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
          }
        } else {
          typeHandlerRegistry.register(typeHandlerClass);
        }
      }
    }
  }
}

5. 总结

创建SqlSessionFactory对象是通过SqlSessionFactoryBuilder的build方法,他会返回它的默认实现类DefaultSqlSessionFactory,而这个类只有一个属性就是Configuration。

solveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
// 调用TypeHandlerRegistry.register(以下是3种不同的参数形式)
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}


# 5. 总结

创建SqlSessionFactory对象是通过SqlSessionFactoryBuilder的build方法,他会返回它的默认实现类DefaultSqlSessionFactory,而这个类只有一个属性就是Configuration。

Configuration是通过XMLConfigBuilder的parse方法来的,而parse方法是通过XPathParser读取mybatis配置文件中的配置进行填充Configuration类。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:在hadoop配置文件hadoop-env.sh中配置 所有主机都操作 \[root@d-01 ~\]# grep -i hadoop_home /opt/hadoop-3.3.2/etc/hadoop/hadoop-env.sh export HADOOP_HOME=/opt/hadoop-3.3.2 JAVA_HOME配置 。 引用\[2\]:复制配置到其他主机 \[root@d-01 ~\]# scp -rp /opt/hadoop-3.3.2/etc/hadoop/core-site.xml d-02:/opt/hadoop-3.3.2/etc/hadoop/core-site.xml \[root@d-01 ~\]# scp -rp /opt/hadoop-3.3.2/etc/hadoop/core-site.xml d-03:/opt/hadoop-3.3.2/etc/hadoop/core-site.xml \[root@d-01 ~\]# scp -rp /opt/hadoop-3.3.2/etc/hadoop/hdfs-site.xml d-02:/opt/hadoop-3.3.2/etc/hadoop/hdfs-site.xml \[root@d-01 ~\]# scp -rp /opt/hadoop-3.3.2/etc/hadoop/hdfs-site.xml d-03:/opt/hadoop-3.3.2/etc/hadoop/hdfs-site.xml 必要操作 。 引用\[3\]:从d-01复制配置到其他节点 \[root@d-01 ~\]# scp /opt/hadoop-3.3.2/etc/hadoop/core-site.xml d-02:/opt/hadoop-3.3.2/etc/hadoop/ \[root@d-01 ~\]# scp /opt/hadoop-3.3.2/etc/hadoop/core-site.xml d-03:/opt/hadoop-3.3.2/etc/hadoop/ \[root@d-01 ~\]# scp /opt/hadoop-3.3.2/etc/hadoop/hdfs-site.xml d-02:/opt/hadoop-3.3.2/etc/hadoop/ \[root@d-01 ~\]# scp /opt/hadoop-3.3.2/etc/hadoop/hdfs-site.xml d-03:/opt/hadoop-3.3.2/etc/hadoop/ 启动journalnode进程 。 问题: org.apache.hadoop.shaded.org.apache.commons.configuration2.Configuration是什么? 回答: org.apache.hadoop.shaded.org.apache.commons.configuration2.Configuration是Apache Hadoop中的一个类,它是用于处理配置文件的接口。它提供了一种方便的方式来读取和解析配置文件,以便在Hadoop集群中进行配置管理。通过使用这个类,用户可以轻松地访问和修改Hadoop的各种配置参数,以满足其特定的需求。这个类的具体实现可以在Hadoop的相关文档中找到。 #### 引用[.reference_title] - *1* *2* *3* [hadoop自动故障转移集群搭建详解](https://blog.csdn.net/weixin_40548182/article/details/124389518)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值