zoukankan      html  css  js  c++  java
  • 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是实现对特定方法拦截的,不在今天的记录范围内。之前的工作相当于完成了这个部分的工作。

  • 相关阅读:
    ue父子组件的数据传递示例
    vue.js 组件之间传递数据
    Vue2.0子同级组件之间数据交互
    assets 和static的区别
    Vue中src属性绑定的问题
    css3设置背景图片的大小
    如何在HTML中插入空格
    css背景图片的设置
    vue动态组件 互相之间传输数据 和指令的定义
    webservice的优缺点
  • 原文地址:https://www.cnblogs.com/kakaxisir/p/4579110.html
Copyright © 2011-2022 走看看