Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。下面以Executor为例。Mybatis在创建Executor对象时会执行下面一行代码:
executor =(Executor) interceptorChain.pluginAll(executor);
interceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每个拦截器依次对executor进行plugin(插入?)代码如下:
* 每一个拦截器对目标类都进行一次代理 * @paramtarget * @return 层层代理后的对象 */ public Object pluginAll(Object target) { for(Interceptor interceptor : interceptors) { target= interceptor.plugin(target); } return target; }
举例:
@Intercepts({@Signature(type = Executor.class, method ="update";, args = {MappedStatement.class, Object.class})}) public class ExamplePlugin implements Interceptor { @Override public Objectintercept(Invocation invocation) throws Throwable { return invocation.proceed(); } @Override public Objectplugin(Object target) { return Plugin.wrap(target, this); } @Override public voidsetProperties(Properties properties) { } }
每一个拦截器都必须实现上面的三个方法,其中:
- 1) Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。
- 2) Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。
- 3) setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。
Plugin.wrap方法
从前面可以看出,每个拦截器的plugin方法是通过调用Plugin.wrap方法来实现的。代码如下:
public staticObject wrap(Object target, Interceptor interceptor) { //从拦截器的注解中获取拦截的类名和方法信息 Map<Class<?>, Set<Method>> signatureMap =getSignatureMap(interceptor); Class<?> type = target.getClass(); //解析被拦截对象的所有接口(注意是接口) Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if(interfaces.length > 0) { //生成代理对象, Plugin对象为该代理对象的InvocationHandler (InvocationHandler属于java代理的一个重要概念,不熟悉的请参考相关概念) returnProxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap)); } returntarget; }
这个Plugin类有三个属性:
private Object target;//被代理的目标类
private Interceptor interceptor;//对应的拦截器
private Map<Class<?>, Set<Method>> signatureMap;//拦截器拦截的方法缓存
我们再次结合(Executor)interceptorChain.pluginAll(executor)这个语句来看,这个语句内部对
executor执行了多次plugin,第一次plugin后通过Plugin.wrap方法生成了第一个代理类,姑且就叫executorProxy1,这个代理类的target属性是该executor对象。第二次plugin后通过Plugin.wrap方法生成了第二个代理类,姑且叫executorProxy2,这个代理类的target属性是executorProxy1...这样通过每个代理类的target属性就构成了一个代理链(从最后一个executorProxyN往前查找,通过target属性可以找到最原始的executor类)。
代理链上的拦截
代理链生成后,对原始目标的方法调用都转移到代理者的invoke方法上来了。Plugin作为InvocationHandler的实现类,他的invoke方法是怎么样的呢?
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass()); if(methods != null && methods.contains(method)) { //调用代理类所属拦截器的intercept方法, return interceptor.intercept(new Invocation(target, method, args)); } returnmethod.invoke(target, args); } catch(Exception e) { throwExceptionUtil.unwrapThrowable(e); } }
在invoke里,如果方法签名和拦截中的签名一致,就调用拦截器的拦截方法。我们看到传递给拦截器的是一个Invocation对象,这个对象是什么样子的,他的功能又是什么呢?
public class Invocation { private Object target; private Methodmethod; private Object[]args; publicInvocation(Object target, Method method, Object[] args) { this.target =target; this.method =method; this.args =args; } ... public Objectproceed() throws InvocationTargetException, IllegalAccessException { returnmethod.invoke(target, args); } }
可以看到,Invocation类保存了代理对象的目标类,执行的目标类方法以及传递给它的参数。
在每个拦截器的intercept方法内,最后一个语句一定是returninvocation.proceed()(不这么做的话拦截器链就断了,你的mybatis基本上就不能正常工作了)。invocation.proceed()只是简单的调用了下target的对应方法,如果target还是个代理,就又回到了上面的Plugin.invoke方法了。这样就形成了拦截器的调用链推进。
总结
我们假设在MyBatis配置了一个插件,在运行时会发生什么?
1) 所有可能被拦截的处理类都会生成一个代理
2) 处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法
3) 执行插接中的拦截方法后,推进目标的执行
如果有N个插件,就有N个代理,每个代理都要执行上面的逻辑。这里面的层层代理要多次生成动态代理,是比较影响性能的。虽然能指定插件拦截的位置,但这个是在执行方法时动态判断,初始化的时候就是简单的把插件包装到了所有可以拦截的地方。
因此,在编写插件时需注意以下几个原则:
1) 不编写不必要的插件;
2) 实现plugin方法时判断一下目标类型,是本插件要拦截的对象才执行Plugin.wrap方法,否者直接返回目标本省,这样可以减少目标被代理的次数。
相关推荐
MyBatis Interceptor 是 MyBatis 框架中的一个重要组件,它允许我们在 SQL 执行过程中插入自定义的行为。在本例中,我们探讨的是如何使用 MyBatis Interceptor 来实现分页功能,这对于处理大数据量的查询是至关重要...
首先,我们需要创建一个实现了 Mybatis `Interceptor` 接口的类,例如 `MybatisInterceptor.java`。这个接口提供了三个方法:`intercept`, `plugin` 和 `setProperties`。在这个案例中,我们将主要关注 `intercept` ...
在MyBatis中,拦截器通过实现`Interceptor`接口来创建。`OpeInfoInterceptor`类就是这样一个拦截器,它使用了`@Intercepts`注解来指定拦截的目标。`@Signature`注解用来精确定义拦截的方法,这里是`Executor`类的`...
Mybatis Interceptor 拦截器的实现 Mybatis Interceptor 拦截器的实现是 Mybatis 框架中的一种重要机制,通过拦截器可以改变 Mybatis 的默认行为,例如 SQL 重写等。拦截器的实现主要基于责任链模式,通过动态代理...
在MyBatis框架中,Interceptor(拦截器)是一种强大的工具,允许我们在执行SQL语句之前或之后进行自定义处理。`mybatis interceptor`是MyBatis的核心组件之一,它允许我们扩展MyBatis的行为,例如处理查询参数、修改...
在“Mybatis拦截器记录数据更新历史记录到MongoDB”这个项目中,我们需要创建一个自定义的拦截器类,该类需要实现`org.apache.ibatis.plugin.Interceptor`接口并覆写`intercept`方法。在这个方法里,我们可以捕获到...
在MyBatis中,Interceptor(拦截器)是一种高级机制,允许开发者在MyBatis执行流程中的特定时刻插入自定义逻辑。在本示例中,Interceptor被用来自动化处理`update_time`字段,即每当执行更新操作时,自动设置该字段...
Mybatis拦截器(Interceptor)是一种插件机制,它允许我们在Mybatis执行SQL语句之前或之后进行自定义操作,比如统计SQL执行时间、添加日志等。拦截器基于Java的动态代理实现,可以拦截Mapper接口方法的调用。 接...
public class MybatisInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // ... } } ``` 在intercept方法中,我们可以获取当前执行的SQL...
本教程将介绍如何使用MyBatis的Interceptor插件实现数据库表查询的分页功能。 首先,理解Interceptor的概念。在MyBatis中,Interceptor是拦截器,它可以拦截MyBatis执行过程中的某些行为,例如SQL的执行、参数的...
public class MyBatisInterceptor implements Interceptor { // ... } ``` 3. 插件运行原理 插件的运行原理是基于责任链模式的。MyBatis框架会将所有插件组成一个链表,插件会依次执行其intercept()方法。如果某个...
1. MyBatis拦截器(MyBatis Interceptor): MyBatis拦截器是一种插件机制,允许我们在SQL执行前或执行后添加自定义行为。通过实现Interceptor接口并覆写intercept方法,我们可以拦截特定的SqlSession方法,如...
public class MybatisInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 拦截器逻辑... return invocation.proceed(); } // 其他方法...
概括 mybatis,动态数据源开关,石英样品项目1.可以通过控制器和任务的其他操作重新安排任务。 2.可以通过反射运行服务类中定义为计划任务的任何方法。 并且可以在“ TaskDomain”中定义任务属性
分页查询是大型应用必备的功能,MyBatis 提供了两种常见的分页方式:一是通过自定义插件(Interceptor),二是使用 MyBatis 提供的 PageHelper 插件。在本项目中,标签提及了 "interceptor",这可能意味着自定义拦截...
在 MyBatis 中,插件主要通过实现 `Interceptor` 接口来创建,这些插件可以拦截 SQL 的执行、参数设置、结果映射等过程,进行定制化处理。例如,你可以创建一个性能监控插件,记录每个 SQL 查询的执行时间,以便分析...
此外,文档还会涵盖MyBatis的插件机制,开发者可以通过实现Interceptor接口创建自定义插件,拦截并修改SqlSession的方法调用,以实现如日志记录、性能分析等功能。 Mapper接口和Mapper XML文件的配合使用是MyBatis...
如本项目要优先于pageHelper拦截器(mybatis Interceptor是倒序执行,所以优先执行的拦截器要晚加载)个人项目,难以测试覆盖所有场景,如使用或参考本项目,请自行测试验证无问题后上线spring-data-jpa插入获取主键...
7. **插件机制**:Mybatis 支持插件拦截,开发者可以通过实现Interceptor接口并配置插件,来增强Mybatis的行为,例如日志记录、性能监控等。 8. **事务管理**:Mybatis 支持JDBC事务管理,也可以与Spring等框架集成...