目录
一、说明
我们在Mybatis源码解析之二 配置文件子节点作用详解 主要讲解了Mybatis配置节点中的properties、settings、typeAliases、typeHandlers这四个节点,下面这篇博文我们来介绍余下的objectFactory、plugins、environments、mappers这些节点
二、objectFactory配置
1、xml配置
Mybatis的官方描述:MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。mybatis给我们提供了一个DefaultObjectFactory对象来实现上述行为, 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现(继承DefaultObjectFactory)。
自己的解释:在使用Mybatis查询的过程中获取的结果对象ResultSet数据通过该对象ObjectFactory的create()方法,要么通过默认构造方法(没有相关的构造器参数),要么在参数映射存在的时候通过参数构造方法来实例化Bean对象,并将结果转换存放在其初始化的bean中,多个可能涉及到集合类的初始化,举例来说我们有一个Book表,从其中查询出来book列表,mybatis查询出来的是List<Book> 则是通过ObjectFacroty会先创建List实例bean(ArrayList),并创建多个Book实例化对象,最终将ResultSet结果信息保存到实力化的List<Book>中。
<!--设置ObjectFactory对象 -->
<objectFactory type="com.soecode.lyf.mybatisConfig.MyObjectFactory">
<!-- 设置相关的属性 -->
<property name="name" value="xieqx" />
</objectFactory>
自定义的ObjectFactory对象,该对象的功能是对查询出来的结果信息Book进行标记mark为true(表示查询过标记为ture)
/**
* 设置自己的自定义的ObjectFactory
* 继承DefaultObjectFactory 重写其中的create()方法
*/
public class MyObjectFactory extends DefaultObjectFactory {
@Override
public <T> T create(Class<T> type) {
T t = super.create(type);
//针对其中查询出来的Book 设置mark为true(没啥实际的作用只是为了验证功能结果)
if(t instanceof Book){
Book book = (Book) t;
book.setMark(true);
}
return t;
}
//创建实例化对象的核心方法
//根据class信息和构造器类型constructorArgTypes 构造器参数 constructorArgs
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
//设置相关的properties 对应<ObjectFactory> 下的<property />
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
}
}
2、代码解析
//注册ObjectFactory
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
//解析<ObjectFactory> 下的type属性
String type = context.getStringAttribute("type");
//获取其中的properties子节点
Properties properties = context.getChildrenAsProperties();
//反射创建对象,并设置其中配置的属性
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
factory.setProperties(properties);
//配置到Configuration
configuration.setObjectFactory(factory);
}
}
3、结果验证
二、plugins配置
1、plugins配置
mybatis中配置的plugins其实就是实现了Interceptor接口的实现类,其实说白了就是拦截器,使用动态代理增强的形式在sql执行过程中动态的拦截方法增强其功能(例如给普通sql增加分页功能)
<!-- 设置拦截器 进行sql语句添加分页查询 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
这里以PageInterceptor为例子做介绍,PageInterceptor是第三方为mybatis提供的分页插件,我们通过简要了解该类来理解mybatis的plugins配置
先看一下Interceptor接口
public interface Interceptor {
// 拦截逻辑,参数是代理类
Object intercept(Invocation invocation) throws Throwable;
// 加载插件,一般使用Plugin.wrap(target, this);加载当前插件
Object plugin(Object target);
// 初始化属性
void setProperties(Properties properties);
}
PageInterceptor实现类 在实现Interceptor 后还需要使用@Intercepts和@Signature来指定拦截器需要拦截的目标(类、方法、参数)
@Signature(
type = Executor.class, 拦截的类只能是: StatementHandler | ParameterHandler | ResultSetHandler | Executor 类或者子类
method = "query", 拦截的类中的方法
args = { //对于重载的方法,需要使用这里的参数类型唯一定位方法
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class}
对于需要拦截的多个方法可以在@Intercepts中添加多个@Signature注解(每一个注解则指定一个拦截的方法),我们可以看到分页拦截器拦截的方法为
Executor的query(MapperStatement,object,RowBounds,ResultHandler)方法
Executor的query(MapperStatement,object,RowBounds,ResultHandler)方法 记性相应的操作
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class PageInterceptor implements Interceptor {
protected Cache<String, MappedStatement> msCountMap = null;
private Dialect dialect;
private String default_dialect_class = "com.github.pagehelper.PageHelper";
private Field additionalParametersField;
private String countSuffix = "_COUNT";
public Object intercept(Invocation invocation) throws Throwable {
具体实现....
}
}
2、解析分析
/**
* 解析配置中的plugins节点
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//遍历<plugins>下的所有子节点<plugin>
for (XNode child : parent.getChildren()) {
//获取节点plugin的interceptor属性
String interceptor = child.getStringAttribute("interceptor");
//获取节点下的properties
Properties properties = child.getChildrenAsProperties();
//根据interceptor获取其中的name(全限定类名)反射实例化拦截器对象
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//为实例化后的拦截器对象设置相关的属性
interceptorInstance.setProperties(properties);
//将生成并完善后的拦截器对象添加到configuration对象
configuration.addInterceptor(interceptorInstance);
}
}
}
3、代码验证
自定义实现了相关的分页拦截器MyPageInterceptor类
参考:https://blog.csdn.net/wf787283810/article/details/77847576实现
执行相关的拦截器
这个方法是实际的拦截逻辑,我们的目的是在这里来实现分页
从当前线程中获取分页对象Page
1、首先获取其中查询出来的总记录数填充到Page
2、对普通的sql添加分页信息
在该方法中针对StatementHandler的prepare方法(预编译sql)执行该拦截器,在拦截方法中进行分页处理
/**
* 自定义分页拦截器
*/
@Intercepts(
{@Signature(
type = StatementHandler.class,
method = "prepare",
args = {
Connection.class
})
}
)
public class MyPageInterceptor implements Interceptor {
private static final String PAGE_PREFIX = "select";
/**
* 执行相关的拦截器
* 这个方法是实际的拦截逻辑,我们的目的是在这里来实现分页
* 从当前线程中获取分页对象Page
* 1、首先获取其中查询出来的总记录数填充到Page
* 2、对普通的sql添加分页信息
* @param invocation
* @return
* @throws Throwable
*/
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler ha