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

  • 图中,有两个需求

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

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

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

1 设计原理

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

2 代码实现

2.1 接口代码

public interface ArithmeticCalculator {
int add(int i, int j);
int sub(int i, int j); int mul(int i, int j);
int div(int i, int j);
}

2.2 实现接口的代码

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}

2.3 测试代码

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

打印出

result:23

result:7

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

2.3 创建动态代理类

public class ArithmeticCalculatorLoggingProxy {
// 要代理的对象
private ArithmeticCalculator target;
public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
super();
this.target = target;
}
// 返回代理对象
public ArithmeticCalculator getLoggingProxy() {
ArithmeticCalculator proxy = null;
ClassLoader loader = target.getClass().getClassLoader();
Class[] interfaces = new Class[] { ArithmeticCalculator.class };
InvocationHandler h = new InvocationHandler() {
/**
* proxy: 代理对象。 一般不使用该对象 method: 正在被调用的方法 args: 调用方法传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 打印日志
System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
// 调用目标方法
Object result = null;
try {
// 前置通知
result = method.invoke(target, args);
// 返回通知, 可以访问到方法的返回值
} catch (NullPointerException e) {
e.printStackTrace();
// 异常通知, 可以访问到方法出现的异常
}
// 后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
// 打印日志
System.out.println("[after] The method ends with " + result);
return result;
}
};
/**
* loader: 代理对象使用的类加载器。
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
*/
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}

2.3.1 测试动态代理

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

打印出:

[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. mysql完美增量备份脚本

    是否因为mysql太大,来回备份浪费资源带宽而发愁,如果想解决这个麻烦就需要增量备份. vi /etc/my.cnf开启日志及定期清理日志log-bin=mysql-binbinlog_format= ...

  2. 接口测试——postman安装

    http://www.jianshu.com/p/dd0db1b13cfc postman的视频终于过审了,https://ke.qq.com/course/229839#tuin=1eb87ef,大 ...

  3. [CSP-S模拟测试48]反思+题解

    状态很垃圾的一场考试.感觉“这么多分就够了”的心态很是在给自己拖后腿. 打开题面,第一页赫然写着:$Claris' Contest$. 吓得我差点手一抖关掉.不过后来想想似乎强到变态的人出的题都不是很 ...

  4. Regex 正则零宽断言

    http://baike.baidu.com/link?url=sLfovpZmIcS5Uz_tiidXoVtjl30Tu3wARMfhnEcbgEGzsb8g1z7dvtGNXTulu1KDodmi ...

  5. STM32 系统架构

    这里所讲的 STM32 系统架构主要针对的 STM32F103 这些非互联型芯片 STM32 主系统主要由四个驱动单元和四个被动单元构成. 四个驱动单元是: 内核 DCode 总线; 系统总线;通用  ...

  6. iOS之CAReplicatorLayer属性简介和使用

    1.CAReplicatorLayer简介 CAReplicatorLayer用于对图层进行复制,包括图层的动画也能复制!可以看着将某一段事务进行重复! #import <QuartzCore/ ...

  7. VS2008的使用

    文章转载自:http://www.cnblogs.com/aduck/archive/2011/11/11/2245460.html 1.如何在vc2008中显示行号 中文版: 菜单-工具-选项 在新 ...

  8. swoole安装异步reids

    /usr/local/php/bin/phpize ./configure --with-php-config=/usr/local/php/bin/php-config --enable-async ...

  9. org-mode记录总结

    org-mode记录总结 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} cod ...

  10. [Fw]How to Add a System Call(Fedora Core 6 Kernel : 2.6.18)

    How to Add a System Call Kernel : 2.6.18編譯環境 : Fedora Core 6 假設要加的system call為 sys_project, 有一個int的輸 ...