增强代码
MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。 默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
/**
* 插件原理
* 在四大对象创建的时候
* 1、每个创建出来的对象不是直接返回的,而是
* interceptorChain.pluginAll(parameterHandler);
* 2、获取到所有的Interceptor(拦截器)(插件需要实现的接口);
* 调用interceptor.plugin(target);返回target包装后的对象
* 3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)
* 我们的插件可以为四大对象创建出代理对象;
* 代理对象就可以拦截到四大对象的每一个执行;
*/
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
1、编写插件
/**
* Description:
*
* @author guizy
* @date 2021/4/22 12:58
*/
@SuppressWarnings("all")
/*
完成插件签名: 告诉MyBatis当前插件用来拦截哪个对象哪个方法
*/
@Intercepts(
{
@Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class)
}
)
public class MyPlugin implements Interceptor {
/**
* 拦截对象目标方法的执行
*
* @param invocation 拦截
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MyPlugin.intercept:" + invocation.getMethod());
// 执行目标方法
Object proceed = invocation.proceed();
return proceed;
}
/**
* 包装目标对象, 包装: 为目标对象创建一个代理对象
*
* @param target
* @return
*/
@Override
public Object plugin(Object target) {
System.out.println("MyPlugin.plugin mybatis将要包装的对象" + target);
// 该方法就是让当前的Interceptor拦截器来包装我们的对象
Object wrap = Plugin.wrap(target, this);
// wrap就是target的动态代理对象
return wrap;
}
/**
* 将插件注册时的property属性设置进来
*
* @param properties
*/
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置的信息: " + properties);
}
}
2、配置插件
3、测试插件
随便执行一个查询方法, 因为拦截的是StatementHandler的parameterize的方法, 因为在mapper.getUser的时候, 底层会调用这个预编译参数方法, 所以会拦截到该方法
@Test
public void testQueryOneUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 使用方式一: 来找到SQL并执行
//User user = sqlSession.selectOne("com.sunny.dao.UserMapper.getUser", 1L);
// 使用方式二: 来找到SQL并执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 实际上底层调用的还是sqlSession的方法,注意:sqlSession调用CRUD方法只能传递一个参数
User user = mapper.getUser(12);
System.out.println(user);
sqlSession.close();
}
打印信息
// 打印了配置的信息
插件配置的信息: {password=8888, username=root}
// 这里在创建4大对象的时候, 都会调用PluginAll方法, 然后里面会拿到所有实现Interceptor的
// 拦截器进行拦截包装
MyPlugin.plugin mybatis将要包装的对象org.apache.ibatis.executor.CachingExecutor@77ec78b9
MyPlugin.plugin mybatis将要包装的对象org.apache.ibatis.scripting.defaults.DefaultParameterHandler@42607a4f
MyPlugin.plugin mybatis将要包装的对象org.apache.ibatis.executor.resultset.DefaultResultSetHandler@25bbf683
MyPlugin.plugin mybatis将要包装的对象org.apache.ibatis.executor.statement.RoutingStatementHandler@7276c8cd
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id = ?;
// 只会为StatementHandler对象创建代理对象, 拦截到parameterize方法
// 因为Executor, parameterHandler, ResultSetHandler的签名不属于StatementHandler
// 所以就没有为它们创建代理对象
MyPlugin.intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
DEBUG [main] - ==> Parameters: 12(Integer)
TRACE [main] - Preparing: SELECT * FROM user WHERE id = ?;
MyPlugin.intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
当前拦截到的对象: org.apache.ibatis.executor.statement.RoutingStatementHandler@7276c8cd
sql语句传入的参数为:1
DEBUG [main] - ==> Parameters: 2(Integer)
TRACE [main] -
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?