需求:

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

分析:

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

场景:

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

举例:

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

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

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

  1. public class Plugin implements InvocationHandler {
  2.  
  3. private Object target;
  4. Plugin(Object target){
  5. this.target = target;
  6. }
  7. public Object invoke(Object proxy, Method method, Object[] args)
  8. throws Throwable {
  9. //睡觉前做的事
  10. Object result = method.invoke(target, args);
  11. //睡觉后做的事
  12. return result;
  13. }
  14. public static Object wrap(Object target){
  15. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  16. target.getClass().getInterfaces(),
  17. new Plugin(target));
  18. }
  19.  
  20. }
  1. public class Main {
  2.  
  3. public static void main(String[] args) {
  4. //要被代理的对象
  5. Sleep sleep = new SleepImpl();
  6. //代理对象
  7. Sleep sleepProxy = (Sleep)Plugin.wrap(sleep);
  8. sleepProxy.sleep();
  9. }
  10. }

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

  1. interface Interceptor {
  2.  
  3. public void interceptBefore()throws Exception;
  4.  
  5. public void interceptAfter()throws Exception;
  6. }
  1. public class SleepBeforeAndAfter implements Interceptor {
  2.  
  3. public void interceptBefore() throws Exception {
  4. System.out.println("之前。。。");
  5. }
  6.  
  7. public void interceptAfter() throws Exception {
  8. System.out.println("之后。。。");
  9.  
  10. }
  11.  
  12. }

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

  1. /**
  2. * 动态代理
  3. *
  4. * @author 魏正迪
  5. * 2018年10月13日
  6. */
  7. public class Plugin implements InvocationHandler {
  8.  
  9. private Object target;
  10. private List<Interceptor> iList = new ArrayList<Interceptor>();
  11.  
  12. Plugin(Object target , List<Interceptor> iList){
  13. this.target = target;
  14. this.iList = iList;
  15. }
  16.  
  17. public Object invoke(Object proxy, Method method, Object[] args)
  18. throws Throwable {
  19. for(Interceptor i :iList){
  20. i.interceptBefore();
  21. }
  22. Object result = method.invoke(target, args);
  23. for(Interceptor i :iList){
  24. i.interceptAfter();
  25. }
  26. return result;
  27. }
  28.  
  29. public static Object wrap(Object target,List<Interceptor> iList){
  30. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  31. target.getClass().getInterfaces(),
  32. new Plugin(target,iList)
  33. );
  34. }
  35.  
  36. }
  1. public class Main {
  2. public static void main(String[] args) {
  3. Sleep sleep = new SleepImpl();
  4. List<Interceptor> iList = new ArrayList<Interceptor>();
  5. iList.add(new SleepBeforeAndAfter());
  6. Sleep sleepProxy = (Sleep)Plugin.wrap(sleep,iList);
  7. sleepProxy.sleep();
  8. }
  9. }

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

  1. public class SleepBeforeAndAfter implements Interceptor {
  2.  
  3. public void interceptBefore(Object target, Method method, Object[] args)
  4. throws Exception {
  5. System.out.println("之前。。。interceptBefore(Object target, Method method, Object[] args)");
  6.  
  7. }
  8.  
  9. public void interceptAfter(Object target, Method method, Object[] args)
  10. throws Exception {
  11. System.out.println("之后。。。interceptAfter(Object target, Method method, Object[] args)");
  12. }
  13.  
  14. }

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

  1. /**
  2. * 拦截对象的包装
  3. * @author 魏正迪
  4. * 2018年10月13日
  5. */
  6. public class Invocation {
  7.  
  8. private Object target;
  9.  
  10. private Object []args;
  11.  
  12. private Method method;
  13.  
  14. Invocation(Object target,Method method,Object[] args){
  15. this.target = target;
  16. this.args = args;
  17. this.method = method;
  18.  
  19. }
  20. /**
  21. * 执行拦截对象的对应的方法
  22. * @return
  23. * @throws Exception
  24. */
  25. public Object process() throws Exception{
  26. return method.invoke(target, args);
  27. }
  28.  
  29. }

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

  1. interface Interceptor {
  2. public Object intercept(Invocation invocation)throws Exception;
  3. }
  1. public class SleepBeforeAndAfter implements Interceptor {
  2.  
  3. public Object intercept(Invocation invocation) throws Exception{
  4. System.out.println("拦截sleep方法要执行的方法之前");
  5. Object result = invocation.process();
  6. System.out.println("拦截sleep方法要执行的方法之后");
  7. return result;
  8. }
  9.  
  10. }

此时Plugin应该是这样的

  1. public class Plugin implements InvocationHandler {
  2.  
  3. private Object target;
  4. private Interceptor interceptor;
  5.  
  6. Plugin(Object target,Interceptor interceptor){
  7. this.target = target;
  8. this.interceptor = interceptor;
  9. }
  10.  
  11. public Object invoke(Object proxy, Method method, Object[] args)
  12. throws Throwable {
  13. Invocation invocation = new Invocation(target,method,args);
  14. return interceptor.intercept(invocation);
  15. }
  16.  
  17. public static Object wrap(Object target,Interceptor interceptor){
  18. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  19. target.getClass().getInterfaces(),
  20. new Plugin(target,interceptor)
  21. );
  22. }
  23.  
  24. }
  1. public class Main {
  2.  
  3. public static void main(String[] args) {
  4. Sleep sleep = new SleepImpl();
  5. SleepBeforeAndAfter s = new SleepBeforeAndAfter();
  6. Sleep sleepProxy1 = (Sleep)Plugin.wrap(sleep,s);
  7. sleepProxy1.sleep();
  8. Sleep sleepProxy2 = (Sleep)Plugin.wrap(sleepProxy1, s);
  9. sleepProxy2.sleep();
  10. }
  11.  
  12. }

到此,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. Go Module实战:基于私有化仓库的GO模块使用实践

    新年开工近一月,2021 年第一期 Open Talk 定档 3 月 18 日晚 8 点,本期我们邀请到了又拍云资深后端开发工程师刘云鹏和我们一起聊聊 Go 最新特性 Go Module 实战. 刘云 ...

  2. Python基础(2)——循环和分支[xiaoshun]

    一.瞎扯 世界上一切的系统都可以被'分支'表示.循环也是分支,只不过又重复之前的'分支'选择罢了.程序如人生,每一次的'分支',每一次的选择,都会有不同的结果: 有的选择止步不前,无限循环: 有的选择 ...

  3. BZOJ_1503 [NOI2004]郁闷的出纳员 【Splay树】

    一 题面 [NOI2004]郁闷的出纳员 二 分析 模板题. 对于全部员工的涨工资和跌工资,可以设一个变量存储起来,然后在进行删除时,利用伸展树能把结点旋转到根的特性,能够很方便的删除那些不符合值的点 ...

  4. mysql 单机多实例重启数据库服务

    1.# cat db.txtbackend 3310base 3320storage 3330payment 3340promotion 3350 2.# cat restart_mysql_slav ...

  5. 1.mysql读写

    一.数据库读取(mysql) 参数 接受 作用 默认 sql or table_name string 读取的表名,或sql语句 无 con 数据库连接 数据库连接信息 无 index_col Int ...

  6. python爬去壁纸网站上的所有壁纸

    import requests as r 2 from bs4 import BeautifulSoup 3 import os 4 base_url = "http://www.win40 ...

  7. Spring Native 项目,把 Spring 项目编译成原生程序!

    Spring Native 是什么 优点 缺点 原生镜像(native image)和常规 JVM 程序的区别 前置条件:GraalVM 支持的语言 关键特性 GraalVM 下的 Java 微服务 ...

  8. java例题_32 取一个整数a从右端开始的4~7位

    1 /*32 [程序 32 左移右移] 2 题目:取一个整数 a 从右端开始的 4-7 位. 3 */ 4 5 /*分析 6 * 从右端开始的第四位相当于原数除以1000后结果的最后一位数, 7 * ...

  9. 「HTML+CSS」--自定义加载动画【005】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  10. [树形DP]加分二叉树

    加 分 二 叉 树 加分二叉树 加分二叉树 题目描述 设一个n个节点的二叉树tree的中序遍历为(l,2,3,-,n),其中数字1,2,3,-,n为节点编号.每个节点都有一个分数(均为正整数),记第j ...