mybatis源码分析:关于XMLConfigBuilder构建Configuration

Scroll Down

1、概述

Mybatis框架从运行的角度其实可以分为两部分:构建运行时环境和执行用户的操作。
在构建DefaultSqlSessionFactory实例时,需要一个Mybatis运行时配置文件的类型-Configuration。这个类是通过XMLConfigBuilder来完成从XML解析到封装成Configuration对象的过程。这个阶段可以理解为构建运行时环境。

2、Mybatis运行时数据Configuration

Configuration的内部属性如下:

protected Environment environment;

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 String logPrefix;
protected Class<? extends Log> logImpl;
protected Class<? extends VFS> vfsImpl;
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;

// XML中的properties标签的内容
protected Properties variables = new Properties();
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 默认为DefaultObjectFactory,XML中配置的ObjectFactory会覆盖这个引用
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;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
*/
protected Class<?> configurationFactory;
// 映射器容器
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// 存储XML中定义的插件
protected final InterceptorChain interceptorChain = new InterceptorChain();
// 类型转换器容器
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
// 存储XML中定义的类型别名
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
// 存储mapper中的接口方法数据
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");
// mapper中的结果
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");

protected final Set<String> loadedResources = new HashSet<>();
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<>();

3、BaseBuilder

BaseBuilder用于构建基础数据。
整体的BaseBuilder结构如下:
image.png
首先所有的BaseBuilder的实现类都存有Mybatis运行时数据即Configuration对象,该对象被声明为final表示创建完成之后,Configuration对象的引用不允许被变更。持有该对象即是该类的一个核心的操作构建Configuration对象的数据。
除此之外,BaseBuilder类还单独持有被final修饰的TypeAliasRegistry和TypeHandlerRegistry对象用于直接向Configuration中注册TypeAlias和TypeHandler。
在BaseBuilder中封装了很多基础方法比如:
(1)、resolveClass()方法:该方法用于根据类名解析Class对象,核心依赖于类加载器。

// 通过别名解析出别名对应的Class对象
protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      // 调用resolveAlias解析
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
}

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

// TypeAliasRegistry中的resolveAlias方法
@SuppressWarnings("unchecked")
// throws class cast exception as well if types cannot be assigned
public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      // 先从缓存中查找
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        // 这里是调用了Mybatis的底层IO类Resources,
        // 而这里的classForName方法实际上只是通过类加载器加载类的全限定名
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
}

(2)、resolveTypeHandler方法:解析TypeHandler

protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
    if (typeHandlerType == null) {
      return null;
    }
    // javaType ignored for injected handlers see issue #746 for full detail
    TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
    if (handler == null) {
      // not in registry, create a new one
      handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
    }
    return handler;
}

对于BaseBuilder这里列举了4个常用的子类。
(1)、XMLConfigBuilder:用于解析Mybatis的运行时配置文件。
(2)、XMLMapperBuilder:用于解析Mybatis的mapper。
(3)、SqlSourceBuilder:用于解析Mybatis的mapper中的SQL。
(4)、XMLStatementBuilder:用于解析mapper中的SQL语句标签的属性。
篇幅限制,具体解析过程会单独详细分析。