在阅读Mybatis源码的时候,对Plugin这边进行了一部分梳理,接下来使用一个例子来说明Mybatis中Plugin的原理.
/*
一个电影的Service,只有一个方法是watchMovie
*/
public interface MovieService {
void watchMovie();
}
public class MovieServiceImpl implements MovieService {
@Override
public void watchMovie() {
System.out.println("看电影中.....");
}
}
/*
在watchMovie之前,我们有些事情需要做,例如将手机静音,买些爆米花和可乐
在watchMovie之后,我们也有些事情需要做,例如将手机调成正常模式,带走垃圾
类似于Mybatis中在进行sql语句之前,或者执行之后,需要做一些处理,而这个处理就是
Plugin完成的逻辑
定义一个Interceptor的接口,在Mybatis中所有的Plugin都实现该接口
*/
import java.lang.reflect.InvocationTargetException;
public interface Interceptor {
// default function
default Object plugin(Object object){
return MovieInvocationHandler.wrapper(object, this);
}
// default function
default public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
before(invocation);
Object object = invocation.process();
after(invocation);
return object;
}
//拦截器在业务之前需要完成的逻辑,是否需要传入参数,可以根据具体情况来定
void before(Invocation invocation);
//拦截器在业务之后需要完成的逻辑,是否需要传入参数,可以根据具体情况来定
void after(Invocation invocation);
}
/*
拦截器1
在看电影之前需要买点爆米花和可乐
在看电影之后需要带走垃圾
*/
import java.lang.reflect.InvocationTargetException;
public class M1Interceptor implements Interceptor{
@Override
public void before(Invocation invocation) {
System.out.println("买些爆米花和可乐");
}
@Override
public void after(Invocation invocation) {
System.out.println("带走垃圾");
}
}
/*
拦截器2
在看电影之前,需要将手机静音
在看电影之后,需要将手机调成正常模式
*/
import java.lang.reflect.InvocationTargetException;
public class M2Interceptor implements Interceptor{
@Override
public void before(Invocation invocation) {
System.out.println("先把手机调成静音");
}
@Override
public void after(Invocation invocation) {
System.out.println("把手机调成正常模式");
}
}
/*
将所有的拦截器,存在一个InterceptorChain中,在Mybatisz中,该对象是在Configuration中
*/
import java.util.ArrayList;
import java.util.List;
public class InterceptorChain {
private List<Interceptor> list = new ArrayList<>();
public void addInterceptor(Interceptor interceptor){
list.add(interceptor);
}
public List<Interceptor> getInterceptorList(){
return list;
}
public Object pluginALl(Object object){
for(Interceptor interceptor : list){
object = interceptor.plugin(object);
}
return object;
}
}
/*
封装一个类,将原本的业务调用放到一个类 -- Invocation
*/
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Invocation {
public Method method;
public Object object;
public Object[] args;
public Invocation(Object object,Method method,Object[] args){
this.object = object;
this.method = method;
this.args = args;
}
public Object process() throws InvocationTargetException, IllegalAccessException {
return method.invoke(object, args);
}
}
/*
这个类就是JDK动态代理需要实现的类 ---InvocationHandler
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MovieInvocationHandler implements InvocationHandler {
public Object target;
public Interceptor interceptor;
public MovieInvocationHandler(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(new Invocation(target, method, args));
}
public static Object wrapper(Object object,Interceptor interceptor){
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(), new MovieInvocationHandler(object, interceptor));
}
}
/*
测试类
*/
public class Test {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
/*
该测试类类似于mybatis中configuration类, configuration类中保存一个interceptor对象
用来保存所有的plugin.
*/
InterceptorChain interceptorChain = new InterceptorChain();
/*
具体的业务,例如mybatis中的Executor,StatementHandler
ParameterHandler,ResultSetHandler 即需要代理的对象
*/
MovieService target = new MovieServiceImpl();
/*
类似于解析mybatis-config.xml中的<plugin>/</plugin>
然后生成对应的plugin对象,然后保存在interceptorChain中
*/
interceptorChain.addInterceptor(new M2Interceptor());
interceptorChain.addInterceptor(new M1Interceptor());
/*
采用责任链和动态代理的设计模式,最后产生一个动态代理的对象
1. 第一次需要代理的对象是 target = new MovieServiceImpl() ---产生的代理对象是
M1Interceptor m1Interceptor = new M1Interceptor();
MovieInvocationHandler handler1 = new MovieInvocationHandler(target1, m1Interceptor);
target1 = new $Proxy0(handler1);
2. 第二次需要代理的对象是target1 --- 产生的代理对象是
M1Interceptor m2Interceptor = new M2Interceptor();
MovieInvocationHandler handler2 = new MovieInvocationHandler(target1, m2Interceptor);
target2 = new $Proxy0(handler2);
3. target = target2 即target2是最后返回的代理对象
*/
target = (MovieService) interceptorChain.pluginALl(target);
/*
调用过程
1. target2.watchMovie() --> handler2.invoke()--> m2Interceptor.interceptor()-->m2Interceptor.before()
2. target1.watchMovie() --> handler1.invoke()--> m1Interceptor.interceptor()-->m1Interceptor.before()
3. target.watchMovie()
4. m1Interceptor.after()
5. m2Interceptor.after()
*/
target.watchMovie();
}
}
/*
动态代理产生的类 $Proxy0
*/
import com.obito.proxy.MovieService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements MovieService {
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void watchMovie() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.obito.proxy.MovieService").getMethod("watchMovie");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}