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

  • 图中,有两个需求

    • 需求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. BZOJ 3653: 谈笑风生(主席树)

    传送门 解题思路 首先对于一个\(a\)来说,要求\(b\)和\(c\),那么\(a,b,c\)一定在一条链上.把\(b\)分类讨论,如果\(b\)是\(a\)的祖宗,这个方案数就很好统计了,就是\( ...

  2. JS-动态加载

    var s = document.createElement('script'); s.setAttribute('src', ''); s.setAttribute('type', 'text/ja ...

  3. 全局配置一个ajax的错误监控

    全局配置一个ajax的错误监控,比如$(document).ajaxError(function(evt, req, settings){    if(req && (req.stat ...

  4. python学习笔记:itsdangerous模块

    使用itsdangerous生成临时身份令牌 安装 pip install itsdangerous 使用 import itsdangerous salt='sdaf'#加盐 t=itsdanger ...

  5. 前端学习笔记——HTML

    1.前端的三把利器 HTML:我们把它比喻成赤裸的一个人 CSS:华丽的衣服 JS/JavaScript:赋予这个人的行为,也就是动起来 2.什么是HTML? HTML 是用来描述网页的一种语言.ht ...

  6. js中继承的实现,原型链的知识点归纳,借用构造函数,组合继承(伪经典继承)

    博客搬迁,给你带来的不便,敬请谅解! http://www.suanliutudousi.com/2017/10/31/js%e4%b8%ad%e7%bb%a7%e6%89%bf%e7%9a%84%e ...

  7. mysql 5.7.20 从frm文件中得到建表语句 (使用 mysql-utilities)

    系统环境  centos 7.2    mysql社区版 5.7.20 mysql-utilities 根据官网的说法,截止到2018年5月30日,实用工具的一些功能在Shell的路线图中,鼓励用户迁 ...

  8. python配置文件configparser详解

    Python中一般需要配置文件,配置文件一般以.cfg, .conf, .ini结尾.配置文件可以将数据库抽离到以 .ini(Windows)结尾的文件中,这样做的优点在于可在配置文件中添加多个数据库 ...

  9. USACO Milk Routing /// 优先队列广搜

    题目大意: 在n个点 m条边的无向图中 需要运送X单位牛奶 每条边有隐患L和容量C 则这条边上花费时间为 L+X/C 求从点1到点n的最小花费 优先队列维护 L+X/C 最小 广搜到点n #inclu ...

  10. NFS挂载error:reason given by server: Permission denied

    首先你得看看你的NFS服务有没有启动 然后你看看你要mount的文件夹有没有在NFS主机共享 然后再看权限对没对