mybatis通过插件 对(Executor、StatementHandler、ParameterHandler、ResultSetHandler
) 这四个 核心对象创建代理进行拦截
对mybatis来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的 动态代理实现的,换句话说,MyBatis中的四大对象都是代理对象
不熟悉 Mybatis 执行流程的,请先阅读: Mybatis 执行流程
Mybatis核心对象介绍
MyBatis的主要的核心部件有以下几个:
Configuration
初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如,插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中
SqlSessionFactory
SqlSession工厂
SqlSession
作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
Executor
MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler
封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
ParameterHandler
负责对用户传递的参数转换成JDBC Statement 所需要的参数,
ResultSetHandler
负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
TypeHandler
负责java数据类型和jdbc数据类型之间的映射和转换
MappedStatement
维护了一条<select|update|delete|insert>节点的封装,
SqlSource
负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql
表示动态生成的SQL语句以及相应的参数信息
代理模式一般是专注一个类,或者某些类,进行增强,或者想在前后加点业务逻辑,那么就可以使用代理模式。
动态代理一般更加灵活,可以任意指定某些要代理的类,然后指定某些方法。
静态代理,一般是在静态代码块中,对某个类进行代理,然后通过代理对象来调用目标对象的方法。常用手段就是 委托,装饰,组合等。
代理模式,一般是聚焦局部的。模板方法模式,一般是聚焦整体,宏观。
模板方法模式,一般是在多态下继承关系上,在父类指定整体流程的执行步骤和骨架,然后制定抽象方法,先 “假设” 某些逻辑存在,把"填空"的抽象方法交给各子类进行实现。
框架通常会在提供扩展点,让核心流程不变的情况下,让用户自己定制化,从而达到灵活的目的。Mybatis 插件机制,亦如是也!
Mybatis 是通过JDK动态代理实现的,然后有些框架是通过装饰模式实现的。
下面是 插件机制的实现原理,首先是应用插件的4个地方,就是 4 大组件实例化的时候,遍历插件进行代理,然后把代理对象返回,这样就增强了。
配置插件,在配置文件中,通过标签,来配置插件, 或者 API的方法 Configuration#addInterceptor 来配置。
对应的 拦截器插件实现类上面一定要有@Intercepts注解,用来声明拦截器要拦截的方法。否则就直接抛异常。
// Plugin#getSignatureMap 源码
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException(
"No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
下面是 源码分析,全程代码注释:
首先是创建插件的实现,是在4大组件实例化的时候,用JDK动态代理的方式,生成的带有拦截器业务代码的 组件代理对象
public class Configuration {
// 拦截器链,维护了一个List, 插件列表
protected final InterceptorChain interceptorChain = new InterceptorChain();
// 默认的 执行器类型
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
/**
* Gets the interceptors.
*
* @return the interceptors
* @since 3.2.2
*/
public List<Interceptor> getInterceptors() {
return interceptorChain.getInterceptors();
}
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
// 获取 DefaultSqlSession 的过程中,需要 实例化 4 大组件, 也正是拦截器要拦截的地方。
// pluginAll 方法会遍历 插件列表,每当有一个符合条件插件,就生成一个代理,JDK的代理方式,
// 代理对象是一层嵌套一层的,总是最后一个插件生成的代理类的invoke方法,总是最先执行。
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,
parameterObject, boundSql);
// 传入原始的 ParameterHandler 对象,吐出来一个 JDK 代理对象
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,
ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,
resultHandler, boundSql, rowBounds);
// 传入原始的 ResultSetHandler 对象,吐出来一个 JDK 代理对象
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,
rowBounds, resultHandler, boundSql);
// 传入原始的 StatementHandler 对象,吐出来一个 JDK 代理对象
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} els