一、先看一个计算器的抽取和实现

  • 图中,有两个需求

    • 需求1-日志:在程序执行期间追踪正在发生的活动
    • 需求2-验证:希望计算器只能处理正数的运算
  • 其中实现的代码片段

从代码片段中可以看出,代码混乱,代码分散,如果日志需求变更,必须修改所有方法。

二、使用动态代理解决以上问题。

1 设计原理

  • 代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.

2 代码实现

2.1 接口代码

  1. public interface ArithmeticCalculator {
  2. int add(int i, int j);
  3. int sub(int i, int j);
  4. int mul(int i, int j);
  5. int div(int i, int j);
  6. }

2.2 实现接口的代码

  1. public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
  2. @Override
  3. public int add(int i, int j) {
  4. int result = i + j;
  5. return result;
  6. }
  7. @Override
  8. public int sub(int i, int j) {
  9. int result = i - j;
  10. return result;
  11. }
  12. @Override
  13. public int mul(int i, int j) {
  14. int result = i * j;
  15. return result;
  16. }
  17. @Override
  18. public int div(int i, int j) {
  19. int result = i / j;
  20. return result;
  21. }
  22. }

2.3 测试代码

  1. public static void main(String[] args) {
  2. ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
  3. int result = arithmeticCalculator.add(11, 12);
  4. System.out.println("result:" + result);
  5. result = arithmeticCalculator.div(21, 3);
  6. System.out.println("result:" + result);
  7. }

打印出

result:23

result:7

这个是原始的代码,接下来,我们加入动态代理

2.3 创建动态代理类

  1. public class ArithmeticCalculatorLoggingProxy {
  2. // 要代理的对象
  3. private ArithmeticCalculator target;
  4. public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
  5. super();
  6. this.target = target;
  7. }
  8. // 返回代理对象
  9. public ArithmeticCalculator getLoggingProxy() {
  10. ArithmeticCalculator proxy = null;
  11. ClassLoader loader = target.getClass().getClassLoader();
  12. Class[] interfaces = new Class[] { ArithmeticCalculator.class };
  13. InvocationHandler h = new InvocationHandler() {
  14. /**
  15. * proxy: 代理对象。 一般不使用该对象 method: 正在被调用的方法 args: 调用方法传入的参数
  16. */
  17. @Override
  18. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  19. String methodName = method.getName();
  20. // 打印日志
  21. System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
  22. // 调用目标方法
  23. Object result = null;
  24. try {
  25. // 前置通知
  26. result = method.invoke(target, args);
  27. // 返回通知, 可以访问到方法的返回值
  28. } catch (NullPointerException e) {
  29. e.printStackTrace();
  30. // 异常通知, 可以访问到方法出现的异常
  31. }
  32. // 后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
  33. // 打印日志
  34. System.out.println("[after] The method ends with " + result);
  35. return result;
  36. }
  37. };
  38. /**
  39. * loader: 代理对象使用的类加载器。
  40. * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
  41. * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
  42. */
  43. proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
  44. return proxy;
  45. }
  46. }

2.3.1 测试动态代理

  1. public static void main(String[] args) {
  2. ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
  3. arithmeticCalculator =
  4. new ArithmeticCalculatorLoggingProxy(arithmeticCalculator).getLoggingProxy();
  5. int result = arithmeticCalculator.add(11, 12);
  6. System.out.println("result:" + result);
  7. result = arithmeticCalculator.div(21, 3);
  8. System.out.println("result:" + result);
  9. }

打印出:

[before] The method add begins with [11, 12]

[after] The method ends with 23

result:23

[before] The method div begins with [21, 3]

[after] The method ends with 7

result:7

2.4 动态代理类代码说明

代理类中, Proxy.newProxyInstance(loader, interfaces, h); 这个类需要传递三个参数

  • loader: 代理对象使用的类加载器。使用目标方法的类加载器即可
  • interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
  • h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法

传递的h中InvocationHandler 的参数介绍:

  • proxy: 代理对象。 一般不使用该对象
  • method: 正在被调用的方法,调用method.invoke(目标对象, args);
  • args: 调用方法传入的参数

    public Object invoke(Object proxy, Method method, Object[] args)

2.5 前置通知,后置通知,返回通知,异常通知

在invoke方法中,可以看出,在方法前后分别可以打印日志,发生异常时,也可以在catch中对异常的处理,这几个不同的位置,分别为 : 前置通知,后置通知,返回通知,异常通知

前置通知 : 调用目标方法之前执行的代码

后置通知 : 调用目标方法之后执行的代码

返回通知 : 可以得到目标方法返回值,从而进一步处理,在method.invoke(目标对象, args);之后

异常通知 : 调用目标方法出异常时,catch中执行的代码,此时,返回通知无法别执行

整个系列项目代码: http://git.oschina.net/nmc5/spring

spring aop的前奏,动态代理 (5)的更多相关文章

  1. Spring AOP中的动态代理

    0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  Spring AOP中的动态代理机制 2.1  ...

  2. 转:Spring AOP中的动态代理

    原文链接:Spring AOP中的动态代理 0  前言 1  动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2  S ...

  3. spring---aop(4)---Spring AOP的CGLIB动态代理

    写在前面 前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程. CGLib全称为Code Generation Library,是一个强 ...

  4. Spring Boot实践——Spring AOP实现之动态代理

    Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践——AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...

  5. java之Spring(AOP)前奏-动态代理设计模式(下)

    在上一章我们看到了,新增的三种类都能实现对原始功能类进行添加功能的事务处理,这三种类就是一个代理. 但是这种代理是写死的,怎样实现对任意接口添加自定义的代理呢? 我们先来看一下之前的代理实现: pub ...

  6. Spring AOP实现原理-动态代理

    目录 代理模式 静态代理 动态代理 代理模式 我们知道,Spring AOP的主要作用就是不通过修改源代码的方式.将非核心功能代码织入来实现对方法的增强.那么Spring AOP的底层如何实现对方法的 ...

  7. Spring AOP关于cglib动态代理

    一: Spring AOP的默认代理方式是jdk动态代理,还有另外一种代理方式是cglib代理,简单说前者基于接口,后者基于继承,基本思路是将被代理对象的类作为父类,然后创建子类来进行方法的调用,调用 ...

  8. spring---aop(2)---Spring AOP的JDK动态代理

    写在前面 spring 事务是springAOP 的一个实现.我们以分析spring的事务,来分析spring的AOP实现. 基本知识 如果目标方法被spring的事务声明,则执行该目标方法的对象就会 ...

  9. Spring AOP系列(二) — 动态代理引言

    接上一篇Spring AOP系列(一)- 代理模式,本篇来聊聊动态代理. 动态代理与静态代理的区别 要想了解动态代理与静态代理的区别,需要有两个前置知识点:java程序是如何执行的以及类加载机制. j ...

  10. Spring AOP系列(三) — 动态代理之JDK动态代理

    JDK动态代理 JDK动态代理核心是两个类:InvocationHandler和Proxy 举个栗子 为便于理解,首先看一个例子: 希望实现这样一个功能:使用UserService时,只需关注自己的核 ...

随机推荐

  1. vue常见面试题

    什么是 mvvm? MVVM 是 Model-View-ViewModel 的缩写.mvvm 是一种设计思想.Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑:View ...

  2. JavaWeb解决中文乱码

    1.Get请求,方案有两种 A:修改Tomcat配置文件 server.xml   URIEncoding="UTF-8" 如:<Connector port="8 ...

  3. tp5.0如何获取header的Authorization值

    tp5.0如何获取header的Authorization值$request->header();好像没有这个值的但是发送请求头部有的 解决方案: 在.htaccess 文件中加入 设置 Set ...

  4. PHP 换行处理

    换行符 unix系列用 \n windows系列用 \r\n mac用 \r PHP中可以用PHP_EOL来替代,以提高代码的源代码级可移植性 如: <?php echo PHP_EOL; // ...

  5. JS对象中属性的增删改查

    对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性 对象的分类:           1.内建对象                 -在ES标准中定义的对象,在任何的ES的实现中都可以 ...

  6. PHP中使用raw格式发送POST请求

    如果请求的参数格式是原生(raw)的内容,应该如何为程序构造一个POST请求函数呢? function http_post($url, $data_string) { $ch = curl_init( ...

  7. 一道面试题:js返回函数, 函数名后带多个括号的用法及join()的注意事项

    博客搬迁,给你带来的不便,敬请谅解! http://www.suanliutudousi.com/2017/11/13/js%E8%BF%94%E5%9B%9E%E5%87%BD%E6%95%B0%E ...

  8. JSON工具类的构建(后端版本)

    前言 在前后端交互的选择上,之前一直采用的是模板引擎(因为我只负责后端). 而这次的一个算是作业吧,前后端都是我,所以就研究了一下JSON交互在java web的应用(主要是前端). 优缺点 前后端耦 ...

  9. python 三方库字典

    参考:https://github.com/jobbole/awesome-python-cn 环境管理 管理 Python 版本和环境的工具 p:非常简单的交互式 python 版本管理工具.官网 ...

  10. SpringMVC(IntelliJ IDEA)(详细操作)

    1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.