需求:

  对原有系统中的方法进行‘拦截’,在方法执行的前后添加新的处理逻辑。

分析:

  不是办法的办法就是,对原有的每个方法进行修改,添加上新的逻辑;如果需要拦截的方法比较少,选择此方法到是会节省成本。但是面对成百上千的方法怎么办?此时需要用到动态代理来实现。

场景:

  例如:对原有的系统添加日志记录、添加性能分析等等。。。

举例:

  如下,需要对Sleep对象的sleep方法进行“拦截”,并在此方法的执行前后添加新的逻辑。想知道‘睡觉前干了什么?睡觉后干了什么?’

interface Sleep {
public void sleep();
}
public class SleepImpl implements Sleep{
public void sleep() {
System.out.println("我于"+new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())+"开始睡觉");
}
}

  创建动态代理类,实现InvocationHandler接口即可。下面的wrap方法:传入要被代理的对象target。返回包装后的代理对象。$Proxy 打断点会看到这样的对象。针对下面的sleepProxy对象,sleepProxy.sleep()调用需要拦截的方法。实际上调用的是Plugin中的invoke方法。invoke方法中的method.invoke(target,args)是真是的调用被代理对象的sleep方法。所以直接在此语句的前后添加相应的逻辑即可满足需要。

public class Plugin implements InvocationHandler {

    private Object target;
Plugin(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//睡觉前做的事
Object result = method.invoke(target, args);
//睡觉后做的事
return result;
}
public static Object wrap(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new Plugin(target));
} }
public class Main {

    public static void main(String[] args) {
//要被代理的对象
Sleep sleep = new SleepImpl();
//代理对象
Sleep sleepProxy = (Sleep)Plugin.wrap(sleep);
sleepProxy.sleep();
}
}

到此,你以为就结束了?不 ,这个仅仅是 说了在睡觉 前后做了什么事,加入还想知道,你在睡觉前后吃了什么东西?当然睡觉后吃东西有点说不通。但 意会就可以了。还有其他巴拉巴拉的需求。你该怎么做?是不是要把所有的 新的逻辑都方法 Plugin中invoke方法中去?这样不合适吧!乱 乱 乱 这样。那咱们能不能抽象出来一个拦截接口,接口中有拦截后要做什么的方法。各种需求只需要实现这个拦截接口即可!

interface Interceptor {

    public void interceptBefore()throws Exception;

    public void interceptAfter()throws Exception;
}
public class SleepBeforeAndAfter implements Interceptor {

    public void interceptBefore() throws Exception {
System.out.println("之前。。。");
} public void interceptAfter() throws Exception {
System.out.println("之后。。。"); } }

然后动态代理类Plugin需要修改

/**
* 动态代理
*
* @author 魏正迪
* 2018年10月13日
*/
public class Plugin implements InvocationHandler { private Object target;
private List<Interceptor> iList = new ArrayList<Interceptor>(); Plugin(Object target , List<Interceptor> iList){
this.target = target;
this.iList = iList;
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
for(Interceptor i :iList){
i.interceptBefore();
}
Object result = method.invoke(target, args);
for(Interceptor i :iList){
i.interceptAfter();
}
return result;
} public static Object wrap(Object target,List<Interceptor> iList){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new Plugin(target,iList)
);
} }
public class Main {
public static void main(String[] args) {
Sleep sleep = new SleepImpl();
List<Interceptor> iList = new ArrayList<Interceptor>();
iList.add(new SleepBeforeAndAfter());
Sleep sleepProxy = (Sleep)Plugin.wrap(sleep,iList);
sleepProxy.sleep();
}
}

现在想对每个对象的方法进行拦截,直接实现Interceptor接口即可!实现其中的两个方法。此时我们新加的逻辑和原有的逻辑并没有什么交集。假如我们想在interceptor中的两个方法中使用被代理对象的各种属性,此时该怎么做?首先想到是将interceptor接口的两个方法添加参数。

public class SleepBeforeAndAfter implements Interceptor {

    public void interceptBefore(Object target, Method method, Object[] args)
throws Exception {
System.out.println("之前。。。interceptBefore(Object target, Method method, Object[] args)"); } public void interceptAfter(Object target, Method method, Object[] args)
throws Exception {
System.out.println("之后。。。interceptAfter(Object target, Method method, Object[] args)");
} }

到此,个人感觉没啥问题了【大牛如发现明显不符的请指出】。但但但但是我们奔着简单明了、面向对象的思想(其实就是mybatis源码插件设计)。我们做出进一步的精简。于是Invocation对象产生了。看到Method对象传进来了。我们是不是可以想到,我们不再 在Plugin中的invoke方法中调用method.invoke(target,args);了,而是在Intercetpor中处理完前后逻辑后进行调用。这样分工明确了。

/**
* 拦截对象的包装
* @author 魏正迪
* 2018年10月13日
*/
public class Invocation { private Object target; private Object []args; private Method method; Invocation(Object target,Method method,Object[] args){
this.target = target;
this.args = args;
this.method = method; }
/**
* 执行拦截对象的对应的方法
* @return
* @throws Exception
*/
public Object process() throws Exception{
return method.invoke(target, args);
} }

此时拦截器Interceptor应该是这样的

interface Interceptor {
public Object intercept(Invocation invocation)throws Exception;
}
public class SleepBeforeAndAfter implements Interceptor {

    public Object intercept(Invocation invocation) throws Exception{
System.out.println("拦截sleep方法要执行的方法之前");
Object result = invocation.process();
System.out.println("拦截sleep方法要执行的方法之后");
return result;
} }

此时Plugin应该是这样的

public class Plugin implements InvocationHandler {

    private Object target;
private Interceptor interceptor; Plugin(Object target,Interceptor interceptor){
this.target = target;
this.interceptor = interceptor;
} 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 wrap(Object target,Interceptor interceptor){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new Plugin(target,interceptor)
);
} }
public class Main {

    public static void main(String[] args) {
Sleep sleep = new SleepImpl();
SleepBeforeAndAfter s = new SleepBeforeAndAfter();
Sleep sleepProxy1 = (Sleep)Plugin.wrap(sleep,s);
sleepProxy1.sleep();
Sleep sleepProxy2 = (Sleep)Plugin.wrap(sleepProxy1, s);
sleepProxy2.sleep();
} }

到此,mybatis插件开发的引言完毕!其实是使用了动态代理和责任链结合的方式。

  

【Mybtais】Mybatis 插件 Plugin开发(一)动态代理步步解析的更多相关文章

  1. MS CRM 2011的自定义和开发(11)——插件(plugin)开发(三)

    http://www.cnblogs.com/StoneGarden/archive/2012/02/06/2340661.html MS CRM 2011的自定义和开发(11)——插件(plugin ...

  2. MS CRM 2011的自定义和开发(11)——插件(plugin)开发(四)

    http://www.cnblogs.com/StoneGarden/archive/2012/02/08/2343294.html MS CRM 2011的自定义和开发(11)——插件(plugin ...

  3. MS CRM 2011的自定义和开发(11)——插件(plugin)开发(一)

    http://www.cnblogs.com/StoneGarden/archive/2012/02/02/2336147.html MS CRM 2011的自定义和开发(11)——插件(plugin ...

  4. MS CRM 2011的自定义和开发(11)——插件(plugin)开发(二)

    http://www.cnblogs.com/StoneGarden/archive/2012/02/06/2339490.html MS CRM 2011的自定义和开发(11)——插件(plugin ...

  5. 从Mybatis源码理解jdk动态代理默认调用invoke方法

    一.背景最近在工作之余,把开mybatis的源码看了下,决定自己手写个简单版的.实现核心的功能即可.写完之后,执行了一下,正巧在mybatis对Mapper接口的动态代理这个核心代码这边发现一个问题. ...

  6. JDK的动态代理深入解析(Proxy,InvocationHandler)(转)

    JDK的动态代理深入解析(Proxy,InvocationHandler)(转) 一.什么是动态代理 动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实.代理一般会实现它所表示的实际对象的 ...

  7. 互联网轻量级框架SSM-查缺补漏第八天(MyBatis插件plugin使用及原理)

    简言:今天进行第八天的记录(只是写了八天).有的时候看的多,有的时候看的少,看的少的时候就攒几天一起写了.而今天这个插件我昨天写了一下午,下班没写完就回去了,今天把尾收了,再加上一个过程图方便下面原理 ...

  8. Mybatis插件Plugin

    Mybatis开源Plugin中最熟知的pagehelper,重点made in China 很多人开始用pagehelper时候,肯定很纳闷,以mysql为例,明明没有加limit语句,为什么打印出 ...

  9. mybatis由浅入深day01_5.3 Mapper动态代理方法

    5.3 Mapper动态代理方法(程序员只需要写mapper接口(相当于dao接口)) 5.3.1 实现原理(mapper代理开发规范) 程序员还需要编写mapper.xml映射文件 程序员编写map ...

随机推荐

  1. 对话对话每日互动CEO方毅:数据智能应用的过去、现在和未来每日互动CEO方毅:数据智能应用的过去、现在和未来

    2008年,大数据的概念被首次提出,麦肯锡全球研究所给出的定义是:大数据是在一种获取.存储.管理.分析方面大大超出了传统数据库软件工具能力范围的数据集合. 2014年,"数据智能" ...

  2. java 动态规划解决最大连续子数列和

    很多动态规划算法非常像数学中的递推.我们如果能找到一个合适的递推公式,就能很容易的解决问题.我们用dp[n]表示以第n个数结尾的最大连续子序列的和,这里第n个数必须在子序列中.于是存在以下递推公式: ...

  3. mysql最经典的语句

    一.基础1.说明:创建数据库CREATE DATABASE database-name2.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备份数 ...

  4. java注解基础入门

    前言 这篇博客主要是对java注解相关的知识进行入门级的讲解,包括**,核心内容主要体现在对java注解的理解以及如何使用.希望通过写这篇博客的过程中让自己对java注解有更深入的理解,在工作中可以巧 ...

  5. CentOS 7 卸载 OpenJDK 安装 OracleJDK

    查看 JDK 安装版本 java -version java version 1.7.0_51 OpenJDK Runtime Environment ( rhel-2.4.5.5.el7-x86_6 ...

  6. Salesforce LWC学习(三十二)实现上传 Excel解析其内容

    本篇参考:salesforce lightning零基础学习(十七) 实现上传 Excel解析其内容 上一篇我们写了aura方式上传excel解析其内容.lwc作为salesforce的新宠儿,逐渐的 ...

  7. 2018年11月16日SQL Server实验内容(触发器实验)

    --注意:先把studentmanager数据库中的所有表用select into命令复制一份, --然后用复制后新表完成下面的实验,同时,对每个触发器都要进行验证. select *into dep ...

  8. 分子量(JAVA语言)

    package 第三章习题; /*  * 给出一种物质的分子式(不带括号)求分子量.  * 本题分子只包含4种原子,分别为C,H,O,N   * 分子量为分别为12.01,1.008,16.00,14 ...

  9. kubernetes 降本增效标准指南| 容器化计算资源利用率现象剖析

    作者:詹雪娇,腾讯云容器产品经理,目前主要负责腾讯云集群运维中心的产品工作. 张鹏,腾讯云容器产品工程师,拥有多年云原生项目开发落地经验.目前主要负责腾讯云TKE集群和运维中心开发工作. 引言 降本增 ...

  10. Python基础之:Python中的内部对象

    目录 简介 内置函数 内置常量 内置类型 逻辑值检测 逻辑值的布尔运算 比较运算 数字类型 整数类型的位运算 整数类型的附加方法 浮点类型的附加方法 迭代器 序列类型 集合类型 映射类型 字典视图对象 ...