精通Mybatis之Configuration配置体系
Posted 木兮君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精通Mybatis之Configuration配置体系相关的知识,希望对你有一定的参考价值。
前言
这次小编要给大家介绍的是Configuration配置体系,不知道大家是怎么学习源码的,如果大家是从xml加载开始学起的话,那Configuration就是第一步了,因为他是将xml解析成Configuration,然后Configuration直接或间接的创建和管理绝大多数组件包括Executor、StatementHandler、Cache、MappedStatement等等。而小编是差不多最后介绍有了前面的基础,Configuration也变得不难了。
Configuration
Configuration 是整个MyBatis的配置体系集中管理中心,前面小编所介绍的Executor、StatementHandler、Cache、MappedStatement等绝大部分组件都是由它直接或间接的创建和管理。此外影响这些组件行为的属性配置也是由它进行保存和维护。如cacheEnabled、lazyLoadingEnabled 等。
对于具体配置可以看mybatis官网的配置。里面非常详尽。
配置概览
这边小编从官网上取出使用比较频繁的配置稍做举例:
这里小编对@Options稍作解释:
@Options作用在接口的方法上,他其实和@Select是搭配使用的,但是分开写也就为什么不在@Select中,第一是我们很少去配置Options,第二分开更加明确写法的容易明白。@Options包含是否使用二级缓存,缓存刷新的策略,使用哪种statementHandler等。
Configuration作用
Configuration 配置来源有三项:
- Mybatis-config.xml 启动文件,全局配置、全局组件都是来源于此。
- Mapper.xml SQL映射(MappedStatement) \\结果集映射(ResultMapper)都来源于此。
- @Annotation SQL映射与结果集映射的另一种表达形式。
总结一下Configuration主要作用如下:
- 存储全局配置信息,其来源于settings(设置)
- 初始化并维护全局基础组件
typeAliases(类型别名)
typeHandlers(类型处理器)
plugins(插件)
environments(环境配置)
cache(二级缓存空间) - 初始化并维护MappedStatement
- 组件构造器,并基于插件进行增强
newExecutor(执行器)
newStatementHandler(JDBC处理器)
newResultSetHandler(结果集处理器)
newParameterHandler(参数处理器)
这里小编介绍一下全局组件,因为上面并没有将所有的组件全部列出,说明一些小编认为比较重要的组件
MapperRegistry
这个组件比较重要,sqlsession获取相应的mapper,mapper明明为接口却可以直接调用方法并获取相应的结果,就靠他了。看下源码:
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
/**
* @since 3.2.2
*/
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
}
首先MapperRegistry 是在configuration中创建的,根据上面源码:他在mappers中配置扫描包或class文件会增加Map<Class<?>, MapperProxyFactory<?>> knownMappers中的值,而MapperProxyFactory就是动态代理工厂,其作用就是生产动态代理类。动态代理在mybatis中这是第二次提到了,第一次是在懒加载机制的时候使用动态代理。
InterceptorChain
拦截器链主要是为了做增强使用,主要是对newExecutor(执行器),newStatementHandler(JDBC处理器),
newResultSetHandler(结果集处理器),newParameterHandler(参数处理器)。后续小编讲插件体系的时候会着重说明
MappedStatement
MappedStatement主要承载了mapper.xml或者是mapper.java文件解析后的sql声明,也就是讲前面文件中所有的设置变成MappedStatement的元素。
Configuration组件构建过程
元素承载
无论是xml 还是配置的注解元素最后都要被转换成JAVA配置属性或对象组件来承载。其对应关系如下:
- 全配置(config.xml) 由Configuration对像属性承载
- sql映射<select|insert…> 或@Select 等由MappedStatement对象承载
- 缓存<cache…> 或@CacheNamespace 由Cache对象承载
- 结果集映射 由ResultMap 对象承载
上面比较抽象,小编画个结构图大家就明白了:
这边大家有没有奇怪resultMap本来应该属于一个namespace下的为什么要放入公共环境配置中去呢?这是因为resultMap的引用可以跨namespace。
配置文件解析过程
配置文件解析需要我们分开讨论,首先来分析XML解析过程。xml配置解析其底层使用dom4j先解析成一棵节点树,然后根据不同的节点类型与去匹配不同的解析器。最终解析成特定组件。
解析器的基类是BaseBuilder 其内部包含全局的configuration 对象,这么做的用意是所有要解析的组件最后都要集中归属至configuration。接下来了解一下每个解析器的作用:
- XMLConfigBuilder :解析config.xml文件,会直接创建一个configuration对象,用于解析全局配置 。
- XMLMapperBuilder :解析Mapper.xml文件,内容包含 等
- MapperBuilderAssistant:Mapper.xml解析辅助,在一个Mapper.xml中Cache是对Statement(sql声明)共享的,共享组件的分配即由该解析实现。
- XMLStatementBuilder:SQL映射解析 即<select|update|insert|delete> 元素解析成MapperStatement。
- SqlSourceBuilder:Sql数据源解析,将声明的SQL解析可执行的SQL。
- XMLScriptBuilder:解析动态SQL数据源当中所设置 SqlNode脚本集。
当然我们测试的时候使用的是SqlSessionFactoryBuilder的build方法,但是他其实什么都不做直接调用的是XMLConfigBuilder.parse的方法,所以上面小编跳过了SqlSessionFactoryBuilder,然后调用晚回来也直接返回一个new DefaultSqlSessionFactory将configuration放进去就好了。
解析mybatis-config.xml的时序图
流程说明:
- 【XmlConfigBuilder】 接收一个config.xml 输入流,然后创建一个空Configuration对象
- 【XmlConfigBuilder】解析全局配置
- 【XmlConfigBuilder】mapperElements解析,通过Resource或url 指定mapper.xml文件
3.1【MapperBuilderAssistant】设置缓存并添加至Configuration
3.2【XMLScriptBuilder】解析生成SQL数据源,包括动态脚本
3.3【XmlMapperBuilder】解析缓存、结果集配置等公共配置
3.4【XmlMapperBuilder】解析Sql映射<select|insert|upate|delete>
3.5【XmlMapperBuilder】构建Statement
解析mapper.java(接口)的时序图
注解解析底层实现是通过反射获取Mapper接口当中注解元素实现。有两种方式一种是直接指定接口名,一种是指定包名然后自动扫描包下所有的接口类。这些逻辑均由Mapper注册器(MapperRegistry)实现。其接收一个接口类参数,并基于该参数创建针对该接口的动态代理工厂,然后解析内部方法注解生成每个MapperStatement 最后添加至Configuration 完成解析。
注意点:当扫描包下的时候,虽然是去扫描mapper.java文件但是他会去扫描相同包下的同一命名的xml,即解析UserMapper.java的时候同时会去解析UserMapper.xml,返回来也是如此。解析xml的时候同样去解析java文件
为什么要加上一个MapperBuilderAssistant?
小编这边是这么思考的。其实无论是xml还是接口类解析,都一开始设置了namespace,这样在添加到configuration的时候就知道讲statementMapper添加到那个命名空间去,否则需要在XMLStatementBuilder和MapperAnnotationBuilder中记录。
总结
今天讲解的configuration体系其实不难,无非就是解析xml或配置文件生成configuration,里面比较重要的点就是mappers配置,mapper.xml或mapper.java怎么变成configuration中的MapperedStatement,还有动态代理类前期是怎么创建进去的。这边对拦截器链没做详细讲解,这个也是mybatis的扩展点,包括分页什么的,小编后续会介绍的,今天的configuration就告一段落了,再接再厉加油!
以上是关于精通Mybatis之Configuration配置体系的主要内容,如果未能解决你的问题,请参考以下文章
mybatis源码-解析配置文件之配置文件Configuration解析(超详细, 值得收藏)