需求:

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

分析:

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

场景:

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

举例:

  如下,需要对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. WPF 基础 - 在模板中找元素

    1. 在 ControlTemplate 中寻找元素 <Window.Resources> <ControlTemplate x:Key="cTmp"> & ...

  2. [笔记] CRT & exCRT

    [笔记] CRT & exCRT 构造法 求多组\(x \equiv r_i (\bmod d_i)\)的解,\(d_i\)互质 余数\((r_i = remainder)\),除数\((d_ ...

  3. Python:垃圾回收

    有很多不同的方法来实现垃圾回收,例如跟踪,引用计数,转义分析,时间戳和心跳信号等.不同的语言依赖于不同的垃圾回收实现,例如,有些将其与编译器和运行时系统集成在一起.而其他语言则可能需要事后设置,甚至可 ...

  4. CobaltStrike 和 Metasploit 联动

    出品|MS08067实验室(www.ms08067.com) 本文作者:掉到鱼缸里的猫(Ms08067内网安全小组成员) 个人觉得CobaltStrike图形化的界面和丰富的功能,是一个超强的后渗透框 ...

  5. iNeuOS工业互联平台,生产过程业务联动控制

    1.概述 工业物联网也好.工业互联网也好或是其他生产系统,反向控制始终无法回避.搞工业最直接.最体现效果的两个方面是采集各种数据和生产过程业务控制,所谓大数据预测和分析,那是仁者见仁.智者见智,下一篇 ...

  6. Makefile基本用法

    来源 https://www.gnu.org/software/make/manual/make.pdf 简单的例子 其中的cc通过链接,间接指向/usr/bin/gcc. Makefile文件中列出 ...

  7. ElasticSearch-03-远行、停止

    在Windows下执行elasticsearch.bat 在Linux下运行./elasticsearch 指定集群名称和节点名称: ./elasticsearch --cluster.name my ...

  8. Trees on the level UVA - 122

    Trees are fundamental in many branches of computer science (Pun definitely intended). Current stateo ...

  9. 1049 Counting Ones

    The task is simple: given any positive integer N, you are supposed to count the total number of 1's ...

  10. 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit

    Given an array of integers nums and an integer limit, return the size of the longest continuous suba ...