JDK动态代理、责任链在mybatis中的应用
以前一直觉得写博客是给别人看的,所以很少分享自己写的东西。这段时间突然意识到博客是给自己看的。
欢迎各位喜欢java的朋友骚扰。
最近在学习mybatis,看了下源代码。翻到了Interceptor的实现,恰好前不久看过JDK的动态代理和责任链,因此来记录一下。
一:JDK的动态代理
概念性质的东西就不谈了,毕竟网上很多。JDK的动态代理要求接口和接口的实现类
public interface Target { public void execute(); }
/** * Target的实现类 * @author wpr * */ public class TargetImpl implements Target { @Override public void execute() { System.out.println("execute"); } }
a.JDK原生的动态代理写法
要求实现InvocationHandler接口,在invoke方法内实现拦截的逻辑(不懂得去看JDK的动态代理)
public class TargetProxy implements InvocationHandler{ Target target; public TargetProxy(Target target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("拦截前"); Object o= method.invoke(target, args); System.out.println("拦截后"); return o; } }
测试的类:
@Test public void test3(){ Target target = new TargetImpl(); target = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new TargetProxy(target)); target.execute(); }
以上就是JDK动态代理的实现,但是存在问题,Proxy.newProxyInstance(..)完全可以交给TargetProxy来处理,于是第二版出现
public class TargetProxy implements InvocationHandler{ //...........上面的代码省略了............... public static Object bind(Target target){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TargetProxy(target)); } }
测试类:
@Test public void test2(){ Target target = new TargetImpl(); target = (Target) TargetProxy.bind(target); target.execute(); }
但还是存在问题,业务代码如果是execute()的话,所有的逻辑都写死在invoke()方法里面了,不符合设计模式的要求。结合面向切面的编程,做如下说明,target.execute()视为业务代码,在invoke()方法前进行插入切面(例如记录日志、开启事务等),设计Interceptor接口
public interface Interceptor { public void intercept(); }
intercept()方法负责处理各种前期准备,下面是Interceptor的两个实现类
public class LogInterceptor implements Interceptor{ @Override public void intercept(){ System.out.println("日志记录开始"); } }
public class TransactionInterceptor implements Interceptor { @Override public void intercept() { System.out.println("事务开启"); } }
代理对象进一步改变,为了形象的说明是拦截器栈,所以我用了Stack,但是感觉使用List(ArrayList更合理一点)
public class TargetProxy implements InvocationHandler{ private Target target; private Stack<Interceptor> interceptorStack; public TargetProxy(Target target, Stack<Interceptor> interceptorStack) { this.target = target; this.interceptorStack = interceptorStack; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for(Interceptor interceptor:interceptorStack){ interceptor.intercept(); } return method.invoke(target, args); } }
在每次执行业务代码execute(...)之前都会拦截,测试代码如下:
@org.junit.Test public void test() { Stack<Interceptor> interceptorStack =new Stack<>(); interceptorStack.add(new LogInterceptor()); interceptorStack.add(new TransactionInterceptor()); Target target = new TargetImpl(); target = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new TargetProxy(target, interceptorStack)); target.execute(); }
接下来更近一步,根据代码的设计准则,将不变的和变化的分离开。我们设计一个Invocation的类,先看下它的实现:
(其实这个地方还可以这样理解:为了在Interceptor中得到被拦截对象的信息,需要定义一种数据结构来表示被拦截的方法,就是Invocation。这样就实现了拦截器Interceptor和具体的对象之间的解耦)
public class Invocation { private Object target; private Method method; private Object[] args; public Invocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } /** * 调用代理类的方法 * @return * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException */ public Object process() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{ return method.invoke(target, args); } //省略了getter和setter }
Invocation类就是将被代理的目标类对立出出来,target表示目标类,method是拦截的方法,args是方法参数,于是新的TargetProxy变成了下面的样子。仅仅是invoke
public class TargetProxy implements InvocationHandler{ private Target target; private Interceptor interceptor; public TargetProxy(Target target,Interceptor interceptor) { this.target = target; this.interceptor= interceptor; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Invocation invocation = new Invocation(target, method, args); return interceptor.intercpt(invocation); } }
同时,要改变Interceptor的行为:
public interface Interceptor { public Object intercpt(Invocation invocation) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; }
具体的实现如下,一定返回invocation.process();要不然拦截就会断掉
public class LogInterceptor implements Interceptor{ @Override public Object intercpt(Invocation invocation) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("打印日志"); return invocation.process(); } }
但是问题又出现了,我们希望目标类只需要了解拦截它的类就可以,并不需要知道它的代理类,于是把target的拦截过程放在Interceptor接口中完成(实际操作交个TargetProxy)。最终我们的Interceptor接口变成了
public interface Interceptor { public Object intercept(Invocation invocation) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; public Object register(Object object); }
public class LogInterceptor implements Interceptor{ @Override public Object intercept(Invocation invocation) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("日志拦截前:"); return invocation.process(); } @Override public Object register(Object target) { return TargetProxy.bind(target, this); } }
public class TargetProxy implements InvocationHandler{ private Object target; private Interceptor interceptor; public TargetProxy(Object target, Interceptor interceptor) { this.target = target; this.interceptor = interceptor; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Invocation invocation = new Invocation(target, method, args); return interceptor.intercept(invocation); } public static Object bind(Object target,Interceptor interceptor){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new TargetProxy(target,interceptor)); } }
到此为止,目标类仅需要知道在执行前应该由谁去拦截它就可以了,测试代码如下:
@org.junit.Test public void test() { Target target = new TargetImpl(); Interceptor interceptor = new LogInterceptor(); target =(Target) interceptor.register(target); target.execute(); }
好处显而易见,在使用时根本不必知道代理的存在,只要定义业务逻辑,和对业务逻辑的拦截(切面),然后把他们绑定在一起就可以了。
二:责任链
以上代码实现了对一个业务的一次拦截,但如果对其进行多次拦截的话就需要用到责任链了(依然略过概念,自己google吧)
public class InterceptorChain { private Stack<Interceptor> interceptors; public InterceptorChain(Stack<Interceptor> interceptors) { this.interceptors = interceptors; } public Object registerAll(Object target){ for(Interceptor interceptor:interceptors){ target = TargetProxy.bind(target, interceptor); } return target; } public void addInterceptor(Interceptor interceptor){ interceptors.add(interceptor); } public Stack<Interceptor> getInterceptor(){ return (Stack<Interceptor>) Collections.unmodifiableCollection(interceptors); } }
registerAll(...)方法来完成对目标的全部代理,一层一层的包裹,测试类
@Test public void interceptorChainTest(){ Stack<Interceptor> interceptors = new Stack<>(); LogInterceptor logInterceptor = new LogInterceptor(); TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); interceptors.add(logInterceptor); interceptors.add(transactionInterceptor); InterceptorChain interceptorChain = new InterceptorChain(interceptors); Target target = new TargetImpl(); target= (Target)interceptorChain.registerAll(target); target.execute(); }
以上内容都比较基础和理论,但mybatis的Interceptor完全是我们这样实现的
三:mybatis的拦截分析
其中大部分和之前的分析一致,Plugin就是TargetProxy,内部实现的代码逻辑也完全相同,Signature是实现对特定方法拦截的,不在今天的记录范围内。之前的工作相当于完成了这个部分的工作。