Mybatis 插件(plugins)简介
Mybatis 插件像
是个拦截器,本质上是JDK动态代理的封装,返回了代理对象,起到了拦截器作用,接下来将按照从0开始实现Mybatis 插件(plugins)功能,再使用一下Mybatis插件应用一下,这里实现并不是与Mybatis Plugin代码一模一样,有一些简化和优化,更加方便理解
简单版拦截器功能 v0.1
定义拦截器
public interface Interceptor {
Object intercept(Object target, Method method, Object[] args) throws Throwable;
}
通用动态代理实现
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
public Plugin(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return interceptor.intercept(target, method, args);
}
}
封装生成代理类工具类
public class PluginUtils {
public static <T> T wrap(T target, Interceptor interceptor) {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new Plugin(target, interceptor));
}
}
编写业务测试类
public interface IPerson {
String sayHello(String name);
void test();
}
public class PersonImpl implements IPerson {
@Override
public String sayHello(String name) {
String msg = "hello, " + name;
System.out.println(msg);
return msg;
}
@Override
public void test() {
System.out.println("test");
}
}
测试类
实现自定义拦截器
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("before.");
Object invoke = method.invoke(target, args);
System.out.println("after.");
return invoke;
}
}
测试类
public class MyTest {
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
测试结果,拦截成功
before.
hello, zhangsan
after.
return: hello, zhangsan
实现注解功能 v0.2
拦截器注解@Intercepts
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
Signature[] value();
}
标记拦截哪些类注解@Signature
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
Class<?> type();
SignatureMethod[] method();
}
标记拦截哪些方法注解@SignatureMethod
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface SignatureMethod {
String method();
Class<?>[] args();
}
优化代理生成工具类,添加解析注解信息并传入给Plugin类
public class PluginUtils {
public static <T> T wrap(T target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new Plugin(target, interceptor, signatureMap));
}
// 解析注解,获取对应的类和方法
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
if (interceptsAnnotation == null) {
throw new RuntimeException(
"No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet<Method>();
signatureMap.put(sig.type(), methods);
}
SignatureMethod[] signatureMethod = sig.method();
for (int i = 0; i < signatureMethod.length; i++) {
try {
Method method = sig.type().getMethod(signatureMethod[i].method(), signatureMethod[i].args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
}
return signatureMap;
}
}
优化自定义拦截器,对方法进行控制,只拦截注解对应的方法
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
public Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(target, method, args);
}
return method.invoke(target, args);
}
}
测试类
实现自定义拦截器
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("3333.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
测试类
public class MyTest {
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor3();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
测试结果,拦截成功
3333.
hello, zhangsan
return: hello, zhangsan
修改拦截器自定义拦截器,改为@SignatureMethod(method = "toString", args = {})
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "test", args = {}) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("3333.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
执行测试类
public class MyTest3 {
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor3();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
personProxy.test();
}
}
测试结果,可以看出,只代理了test方法,没有代理sayHello
hello, zhangsan
return: hello, zhangsan
3333.
test
若想代理,需要修改注解,再次执行测试类即可
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }),
@SignatureMethod(method = "test", args = { }) }) })
拦截器链实现 v0.3
链式封装
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object wrapAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = PluginUtils.wrap(target, interceptor);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}
添加实现自定义拦截器
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("3333.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor4 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("4444.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor5 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("5555.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
测试类
public class MyTest2 {
public static void main(String[] args) {
InterceptorChain chain = new InterceptorChain();
chain.addInterceptor(new MyInterceptor3());
chain.addInterceptor(new MyInterceptor4());
chain.addInterceptor(new MyInterceptor5());
IPerson person = new PersonImpl();
IPerson personProxy = (IPerson) chain.wrapAll(person);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
测试结果,多拦截器效果
5555.
4444.
3333.
hello, zhangsan
return: hello, zhangsan
可以思考一下为什么倒序输出5555. 4444. 3333. ?
拦截器链实现 v0.4
封装一下拦截器Interceptor的参数
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;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
修改拦截器Interceptor参数
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
}
插件Plugin封装修改return interceptor.intercept(new Invocation(target, method, args));
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
public Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
}
}
修改自定义拦截器实现Object invoke = invocation.proceed();
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("3333.");
Object invoke = invocation.proceed();
return invoke;
}
}
测试类
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor3();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
测试类
3333.
hello, zhangsan
return: hello, zhangsan
接下来分析一下官网的使用
Mybatis官网插件(plugins)分析
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
所有我们注册的类只能是上面的四个接口,ExamplePlugin.java
拦截器例子
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need, 拦截前
Object returnObject = invocation.proceed();
// implement post processing if need, 拦截后
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
那么从源代码实现来看,为什么Mybatis plugins功能只支持以前四个接口呢,我们可以从类org.apache.ibatis.session.Configuration
中明白
- Executor,这个一个获取Executor的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的Executor对象为代理对象
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);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
- StatementHandler,这个一个获取StatementHandler的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的StatementHandler对象为代理对象
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 = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
- ParameterHandler,这个一个获取ParameterHandler的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的ParameterHandler对象为代理对象
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,
parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
- ResultSetHandler ,这个一个获取ResultSetHandler 的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的ResultSetHandler 对象为代理对象
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 = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
完整代码例子
- 执行命令从github上面拉取代码:git clone git@github.com:dengjili/mybatis-3-mybatis-3.4.6.git
- 相关代码目录
src/test/java priv.mybatis.example03
src/test/java priv.mybatis.example04
src/test/java priv.mybatis.example05