这次小编要给大家介绍的是Configuration配置体系,不知道大家是怎么学习源码的,如果大家是从xml加载开始学起的话,那Configuration就是第一步了,因为他是将xml解析成Configuration,然后Configuration直接或间接的创建和管理绝大多数组件包括Executor、StatementHandler、Cache、MappedStatement等等。而小编是差不多最后介绍有了前面的基础,Configuration也变得不难了。
Configuration 是整个MyBatis的配置体系集中管理中心,前面小编所介绍的Executor、StatementHandler、Cache、MappedStatement等绝大部分组件都是由它直接或间接的创建和管理。此外影响这些组件行为的属性配置也是由它进行保存和维护。如cacheEnabled、lazyLoadingEnabled 等。
对于具体配置可以看mybatis官网的配置。里面非常详尽。
这边小编从官网上取出使用比较频繁的配置稍做举例:
这里小编对@Options稍作解释:
@Options作用在接口的方法上,他其实和@Select是搭配使用的,但是分开写也就为什么不在@Select中,第一是我们很少去配置Options,第二分开更加明确写法的容易明白。@Options包含是否使用二级缓存,缓存刷新的策略,使用哪种statementHandler等。
Configuration 配置来源有三项:
总结一下Configuration主要作用如下:
这里小编介绍一下全局组件,因为上面并没有将所有的组件全部列出,说明一些小编认为比较重要的组件
这个组件比较重要,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
拦截器链主要是为了做增强使用,主要是对newExecutor(执行器),newStatementHandler(JDBC处理器),
newResultSetHandler(结果集处理器),newParameterHandler(参数处理器)。后续小编讲插件体系的时候会着重说明
MappedStatement主要承载了mapper.xml或者是mapper.java文件解析后的sql声明,也就是讲前面文件中所有的设置变成MappedStatement的元素。
无论是xml 还是配置的注解元素最后都要被转换成JAVA配置属性或对象组件来承载。其对应关系如下:
上面比较抽象,小编画个结构图大家就明白了:
这边大家有没有奇怪resultMap本来应该属于一个namespace下的为什么要放入公共环境配置中去呢?这是因为resultMap的引用可以跨namespace。
配置文件解析需要我们分开讨论,首先来分析XML解析过程。xml配置解析其底层使用dom4j先解析成一棵节点树,然后根据不同的节点类型与去匹配不同的解析器。最终解析成特定组件。
解析器的基类是BaseBuilder 其内部包含全局的configuration 对象,这么做的用意是所有要解析的组件最后都要集中归属至configuration。接下来了解一下每个解析器的作用:
当然我们测试的时候使用的是SqlSessionFactoryBuilder的build方法,但是他其实什么都不做直接调用的是XMLConfigBuilder.parse的方法,所以上面小编跳过了SqlSessionFactoryBuilder,然后调用晚回来也直接返回一个new DefaultSqlSessionFactory将configuration放进去就好了。
解析mybatis-config.xml的时序图
流程说明:
解析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就告一段落了,再接再厉加油!