zoukankan      html  css  js  c++  java
  • 带你了解 MyBatis 插件设计演化过程

    原文地址:带你了解 MyBatis 插件设计演化过程
    博客地址:http://www.extlight.com

    之前写过一篇 《Mybatis 插件实现动态设置参数》 文章,介绍了 Mybatis 插件的扩展和使用。笔者在空闲时间梳理了一下 MyBatis 插件的工作原理,在此记录和分享其插件功能代码的演化过程。

    一、原始代码

    我们简略 MyBatis 执行 SQL 的步骤,下边的原始代码是依靠 Executor 执行 SQL 语句。

    interface Executor {
    	
    	void execute(String sql);
    }
    
    class DefaultExecutor implements Executor {
    
    	@Override
    	public void execute(String sql) {
    		System.out.println("执行:" + sql);
    	}
    }
    
    public class Demo {
    
    	public static void main(String[] args) {
    		Executor executor = new DefaultExecutor();
    		executor.execute("select * from t_user");
    	}
    }
    

    假设,我们需要 Executor 在执行 SQL 语句的前后打印出当前时间戳(方法增强),那该如何操作?

    针对方法增强的情况,有 3 个方案:

    1. 修改源码:
      修改 execute 方法,在执行 SQL 前后加入日志打印方法。违背开源-封闭原则且维护繁琐(如扩展的是第三方jar,需修改源码再打包)。且作为通用组件开发也不合适此方案。

    2. 使用继承:
      继承父类,重写方法,属于纵向方法增强。只对一个类产生作用,如要对一批类进行方法增强,需要创建多个子类,扩展性不好。

    3. 动态代理:
      动态生成代理对象,代替目标对象执行操作,无需修改源码,易扩展和维护。

    二、动态代理

    接下来我们使用动态代理方案,创建 TargetProxyHandler 实现类和 TargetProxyFactory 工厂类。

    class TargetProxyHandler implements InvocationHandler {
    	
    	private Object target;
    
    	public TargetProxyHandler(Object target) {
    		this.target = target;
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("执行前:" + System.nanoTime());
    		Object result = method.invoke(target, args);
    		System.out.println("执行后:" + System.nanoTime());
    		return result;
    	}
    	
    }
    
    class TargetProxyFactory {
    	
    	public static Object newProxy(Object target) {
    		return Proxy.newProxyInstance(
    				target.getClass().getClassLoader(), 
    				target.getClass().getInterfaces(), 
    				new TargetProxyHandler(target));
    	}
    }
    
    public class Demo {
    
    	public static void main(String[] args) {
    	    Executor target = new DefaultExecutor();
    	    
    		Executor executor = (Executor) TargetProxyFactory.newProxy(target);
    		executor.execute("select * from t_user");
    	}
    }
    

    执行结果:

    执行前:5344823093500
    执行:select * from t_user
    执行后:5344823399900
    

    TargetProxyHandler 实现类用于执行被代理对象的目标方法( execute ),TargetProxyFactory 负责创建代理对象。

    这样,我们使用动态代理实现了日志打印的需求。但又产生新的问题:

    现在执行被代理对象的 execute 方法前后都有日志打印,将来我们还想对其进行方法增强(如去掉日志或添加事务)。还是得修改 TargetProxyHandler 源码,但其作为代理对象的执行方法的通用组件,不应该参杂业务代码,那我们应该处理呢?

    这个问题的根源在于 invoke 方法上。我们需要把其执行内容抽离出来封装到单独的组件中,组件提供方法调用即可。

    这种解决方案就是我们熟知的拦截器。

    三、拦截器

    我们需要创建 Interceptor 接口与 LogInterceptor 实现类,将 TargetProxyHandlerinvoke 方法替换成拦截器的调用方法。

    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;
    	}
    	
    	public Object process() throws Exception {
    		return method.invoke(target, args);
    	}
    }
    
    interface Interceptor {
    	
    	Object intercept(Invocation invocation) throws Exception;
    }
    
    class LogInterceptor implements Interceptor {
    
    	@Override
    	public Object intercept(Invocation invocation) throws Exception {
    		System.out.println("执行前:" + System.nanoTime());
    		Object result = invocation.process();
    		System.out.println("执行后:" + System.nanoTime());
    		return result;
    	}
    }
    
    class TargetProxyHandler implements InvocationHandler {
    	
    	private Object target;
    	
    	private Interceptor interceptor;
    	
    	public TargetProxyHandler(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);
    	}
    }
    
    class TargetProxyFactory {
    	
    	public static Object newProxy(Object target, Interceptor interceptor) {
    		return Proxy.newProxyInstance(
    				target.getClass().getClassLoader(), 
    				target.getClass().getInterfaces(), 
    				new TargetProxyHandler(target, interceptor));
    	}
    }
    
    public class Demo {
    
    	public static void main(String[] args) {
    	    Executor target = new DefaultExecutor();
    	    
    		Interceptor logIntercetor = new LogInterceptor();
    		
    		Executor executor = (Executor) TargetProxyFactory.newProxy(target, logIntercetor);
    		executor.execute("select * from t_user");
    	}
    }
    
    

    执行结果同上。

    上述代码中,我们将方法增强的代码从 TargetProxyHandler.invoke 剥离抽取到 LogInterceptor.intercept 中。TargetProxyHandler 得到释放,LogInterceptor 作为业务代码可由业务决定其实现逻辑。

    注意,我们还有一个问题没解决,正如上述描述的,如果我们还要新增一个事务开启,提交的功能,代码如何实现呢?

    在上边的代码中,我们定义了 Interceptor 接口,LogInterceptor 实现该接口用于处理日志方法增强的业务。依瓢画葫芦,我们可以创建 TransactionInterceptor 类实现 Interceptor 接口用于处理事务。

    问题出现了,现在有两个拦截器,而 TargetProxyFactory 工厂类只能接受一个拦截器对象,我们如何同时使用这两个拦截器呢?

    当然是使用拦截器链!

    四、拦截器链

    创建 InterceptorChain 类封装拦截器。

    interface Interceptor {
    	
    	Object intercept(Invocation invocation) throws Exception;
    	
    	Object plugin(Object target);
    }
    
    class LogInterceptor implements Interceptor {
    
    	@Override
    	public Object intercept(Invocation invocation) throws Exception {
    		System.out.println("执行前:" + System.nanoTime());
    		Object result = invocation.process();
    		System.out.println("执行后:" + System.nanoTime());
    		return result;
    	}
    
    	@Override
    	public Object plugin(Object target) {
    		return TargetProxyFactory.newProxy(target, this);
    	}
    }
    
    class TransactionInterceptor implements Interceptor {
    
    	@Override
    	public Object intercept(Invocation invocation) throws Exception {
    		System.out.println("事务提交前");
    		Object result = invocation.process();
    		System.out.println("事务提交后");
    		return result;
    	}
    
    	@Override
    	public Object plugin(Object target) {
    		return TargetProxyFactory.newProxy(target, this);
    	}
    }
    
    class InterceptorChain {
    
    	private List<Interceptor> interceptors = new ArrayList<>();	
    	
    	public void addInterceptor(Interceptor interceptor) {
    		this.interceptors.add(interceptor);
    	}
    	
    	public Object pluginAll(Object target) {
    		for (Interceptor interceptor : interceptors) {
    			target = interceptor.plugin(target);
    		}
    		return target;
    	}
    }
    
    class TargetProxyHandler implements InvocationHandler {
    	
    	private Object target;
    	
    	private Interceptor interceptor;
    	
    	public TargetProxyHandler(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);
    	}
    	
    }
    
    class TargetProxyFactory {
    	
    	public static Object newProxy(Object target, Interceptor interceptor) {
    		return Proxy.newProxyInstance(
    				target.getClass().getClassLoader(), 
    				target.getClass().getInterfaces(), 
    				new TargetProxyHandler(target, interceptor));
    	}
    }
    
    public class Demo {
    
    	public static void main(String[] args) {
    		Executor target = new DefaultExecutor();
    		
    		Interceptor logIntercetor = new LogInterceptor();
    		Interceptor transactionIntercetor = new TransactionInterceptor();
    		
    		InterceptorChain interceptorChain = new InterceptorChain();
    		interceptorChain.addInterceptor(logIntercetor);
    		interceptorChain.addInterceptor(transactionIntercetor);
    		
    		Executor executor = (Executor) interceptorChain.pluginAll(target);
    		executor.execute("select * from t_user");
    	}
    }
    

    执行结果:

    事务提交前
    执行前:5467232073200
    执行:select * from t_user
    执行后:5467232129200
    事务提交后
    

    除了新增了 InterceptorChain,我们还修改 Interceptor 接口,为其定义了 plugin 方法,由拦截器自己维护创建代理对象。

    InterceptorChain 中定义 pluginAll 方法,用于遍历创建代理对象(第一次遍历,被代理对象是 target,创建出代理对象为A;第二次遍历,被代理对象是A,创建出代理对象是B)。

    至此,MyBatis 插件设计演化过程结束。当然,笔者是指简单的梳理演变过程,MyBatis 插件实际的执行代码要复杂很多,但思想和原理是大致相同的。

  • 相关阅读:
    out/host/linuxx86/obj/EXECUTABLES/aapt_intermediates/aapt 64 32 操作系统
    linux 查看路由器 电脑主机 端口号 占用
    linux proc进程 pid stat statm status id 目录 解析 内存使用
    linux vim 设置大全详解
    ubuntu subclipse svn no libsvnjavahl1 in java.library.path no svnjavahl1 in java.library.path no s
    win7 安装 ubuntu 双系统 详解 easybcd 工具 不能进入 ubuntu 界面
    Atitit.json xml 序列化循环引用解决方案json
    Atitit.编程语言and 自然语言的比较and 编程语言未来的发展
    Atitit.跨语言  文件夹与文件的io操作集合  草案
    Atitit.atijson 类库的新特性设计与实现 v3 q31
  • 原文地址:https://www.cnblogs.com/moonlightL/p/14982350.html
Copyright © 2011-2022 走看看