预读
mybatis通过插件 对(Executor、StatementHandler、ParameterHandler、ResultSetHandler) 这四个 核心对象创建代理进行拦截
对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 MappedStatement维护了一条<select|update|delete|insert>节点的封装,
- SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
- BoundSql 表示动态生成的SQL语句以及相应的参数信息
准备
/**
* 自定义mybatis拦截器
*/
@Intercepts({
// 拦截指定接口的指定方法
@Signature(type= StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class}),
// @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class StatementInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("对方法进行增强....");
// 执行原方法
Object result = invocation.proceed();
return result;
}
@Override
public Object plugin(Object target) {
// 调用插件,创建目标类的代理对象
System.out.println("为 "+target+" 创建代理对象");
return Plugin.wrap(target,this);
}
// 为拦截器设置参数值
@Override
public void setProperties(Properties properties) {
}
}
<configuration>
<!--加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>
<!--开启全局的二级缓存配置-->
<!-- <settings>-->
<!-- <setting name="lazyLoadingEnabled" value="true"/>-->
<!-- </settings>-->
<!--给实体类的全限定类名给别名-->
<typeAliases>
<!--给单独的实体起别名-->
<!-- <typeAlias type="com.xiaoxuu.pojo.User" alias="user"></typeAlias>-->
<!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
<package name="com.xiaoxu.pojo"/>
</typeAliases>
<plugins>
<plugin interceptor="com.xiaoxu.interceptor.StatementInterceptor"></plugin>
</plugins>
<!--environments:运行环境-->
<environments default="development">
<environment id="development">
<!--当前事务交由JDBC进行管理-->
<transactionManager type="JDBC"></transactionManager>
<!--当前使用mybatis提供的连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射配置文件-->
<mappers>
<!-- <mapper class="com.xiaoxuu.mapper.IOrderMapperr"></mapper>-->
<package name="com.xiaoxu.mapper"/>
</mappers>
</configuration>
interceptor执行原理剖析
1、mybatis在解析配置文件的时候,会创建拦截器,放入configuration中
/**
* 解析 XML
*
* 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
*
* @param root 根节点
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析 <properties /> 标签
propertiesElement(root.evalNode("properties"));
// 解析 <settings /> 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载自定义的 VFS 实现类
loadCustomVfs(settings);
// 解析 <typeAliases /> 标签
typeAliasesElement(root.evalNode("typeAliases"));
// 解析 <plugins /> 标签
pluginElement(root.evalNode("plugins"));
// 解析 <objectFactory /> 标签
objectFactoryElement(root.evalNode("objectFactory"));
// 解析 <objectWrapperFactory /> 标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 <reflectorFactory /> 标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 赋值 <settings /> 到 Configuration 属性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 标签
environmentsElement(root.evalNode("environments"));
// 解析 <databaseIdProvider /> 标签
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析 <typeHandlers /> 标签
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析 <mappers /> 标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
Configuration中的interceptorChain
/**
* 拦截器链
*
* @author Clinton Begin
*/
public class InterceptorChain {
/**
* 拦截器数组
*/
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 应用所有插件
*
* @param target 目标对象
* @return 应用结果
*/
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
2、在创建sqlSession的时候,会创建executor,从Configuration中获取拦截器链,遍历拦截器链interceptorChain,对executor进行代理
同理 StatementHandler,paramenterHandler,ResultHandler也会通过拦截器链的方式创建代理对象,这些handler默认是需要的时候创建,而不是一开始就创建
// 创建 ParameterHandler 对象
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
// 创建 ParameterHandler 对象
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 应用插件
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
// 创建 ResultSetHandler 对象
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
// 创建 DefaultResultSetHandler 对象
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 应用插件
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
// 创建 StatementHandler 对象
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创建 RoutingStatementHandler 对象
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 应用插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
3、获取代理对象,代理对象调用方法,默认通过executor执行,默认先cacheExecutor,然后 baseExecutor
二级缓存所在的cacheExecutor
一级缓存所在的baseExecutor
4、通过executor执行,默认会创建statmentHandler处理sql编译,参数设置,结果处理‘
预编译sql,由于是代理对象statementHandler,那么走invoke
由于我们指定了要对StatementHandler的prepare方法进行拦截调用,所以此时invoke方法中会判断是否 对该方法进行拦截。 如果拦截的方法是目标方法,那么走拦截器
拦截器增强方法
走完增强方法,走原方法
/**
* 方法调用信息
*
* @author Clinton Begin
*/
public class Invocation {
/**
* 目标对象
*/
private final Object target;
/**
* 方法对象
*/
private final Method method;
/**
* 方法参数数组
*/
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
/**
* 调用方法
*
* @return 调用结果
*/
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}