插件:在Mybatis的四大对象调度的时候插入我们的代码去执行一些特殊的需求以满足特殊场景的需求。
在Mybatis中使用插件,我们需要实现Interceptor接口。
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.plugin;
import java.util.Properties;
/**
* @author Clinton Begin
*/
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
Intercept方法:它将直接覆盖你所拦截对象原有的方法,因此它是插件的核心方法。Intercept里面有个参数 Invocation对象,通过它可以反射调度原来对象的方。
plugin方法: target是被拦截对象,它的作用是给被拦截对象生成一个代理对象,并返回它。为了方便 My Batis使用 org.apache ibatis plugin. Plugin中的wap静态( (stati方法提供生成代理对象,我们往往使用 plugin方法便可以生成一个代理对象了。当然也可以自定义。
setProperties方法:允许在 plugin元素中配置所需参数,方法在插件初始化的时候就被调用了一次,然后把插件对象存入到配置中,以便后面再取出。
插件的初始化是在Mybatis初始化的时候完成的。保存在Configuration中。
插件初始化完成。
插件用的是责任链模式。责任链是一个对象在mybatis中可能是四大对象中的一个,在多个角色中传递,处在传递链上的任何角色都由机会处理它。
Mybatis的责任链是由interceptorChain去定义的。
如 创建执行器的时候:
plugin方法是生成代理对象的方法,当它取出插件的时候是从 Configuration对象中去取出的。从第一个对象(四大对象中的一个)开始,将对象传递给了 plugin方法,然后返回一个代理;如果存在第二个插件,那么我们就拿到第一个代理对象,传递给 plugin方法再返回第一个代理对象的代理…1此类推,有多少个拦截器就生成多少个代理对象。这样一个插件都可以拦截到真实的对象了。这就好比每一个插件都可以一层层处理被拦截的对象。MyBatis的四大对象也是这样处理的。
代理对象可以使用java自带的jdk动态代理。
在编写自定义插件的时候,先需要了解一个工具类方便获取响应的数据信息。MetaObject 元数据对象。
例如:
获取对象的属性值。
插件的开发需要三步骤:
1.需要确定拦截的签名
2.实现拦截方法
3.配置和运行监测
签名:
1.确定需要拦截的对象
首先要根据功能来确定你需要拦截什么对象
Executor是执行SQL的全过程,包括组装参数,组装结果集返回和执行SQL过程,都可以拦截,较为广泛。
StatementHandler是执行SQL的过程,我们可以重写执行SQL的过程。这是我最常用的拦截对象。
ParameterHandler,很明显它主要是拦截执行SQL的参数组装,可以重写组装参数规则。
Resultsethandler用于拦截执行结果的组装,你可以重写组装结果的规则。
如果我们清楚需要拦截的是 StatementHandler对象,应该在预编译SQL之前,修改SQL使结果返回数量被限制。
2.确定拦截该对象那个方法和参数。
如:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
拦截执行器Executor中的query方法。
比如PageInterceptor类 分页插件
@Intercepts(
{
@Signature(type = Executor.class,//确定要拦截的对象
method = "query",//确定要拦截的方法
args = {MappedStatement.class, Object.class})//拦截方法的参数
}
)
public class MyInterceptor implements Interceptor {
接着需要实现拦截方法。
package com.xxx.service;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
/**
* @program: maobc-small_routine
* @description:
* @author: z.hw
* @create: 2018-12-08 16:05
**/
@Intercepts(
{
@Signature(type = Executor.class,//确定要拦截的对象
method = "query",//确定要拦截的方法
args = {MappedStatement.class, Object.class})//拦截方法的参数
}
)
public class MyInterceptor implements Interceptor {
Properties properties=null;
/**
* 代替拦截对象方法的内容
* @param invocation 责任链对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("before.. ");
//如果当前代理的是一个非代理对象,那么它就回调用真实拦截对象的方法,如果不是
//它会调度下个插件代理对象的 invoke方法
Object proceed = invocation.proceed();
System.err.println("after.. ");
return proceed;
}
/**
* 生成对象的代理,这里常用 MyBatis提供的 Plugin类的wrap方法
* @param target 被代理的对象
* @return
*/
@Override
public Object plugin(Object target) {
//使用 MyBatis提供的 Plugin类生成代理对象
System.err.println("调用生成代理对象..");
Object wrap = Plugin.wrap(target, this);
return wrap;
}
/**
* 获取插件配置的属性,我们在Mybatis的配置文件中的配置
*
* @param properties 配置的参数
*/
@Override
public void setProperties(Properties properties) {
System.out.println(properties.get("dbType"));
this.properties=properties;
}
}
配置:
<plugins>
<plugin interceptor="">
<property name="dbType" value="mysql" />
</plugin>
</plugins>
可以参看PageInterceptor源码。