写一个计算类,计算前后需要打印日志。

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

    @Override
public int add(int i, int j) {
// TODO Auto-generated method stub
System.out.println("The method add begins with[" + i + "," + j + "]");
int result = i + j;
System.out.println("The method add ends with " + result);
return result;
} @Override
public int sub(int i, int j) {
// TODO Auto-generated method stub
System.out.println("The method sub begins with[" + i + "," + j + "]");
int result = i - j;
System.out.println("The method sub ends with " + result);
return result;
} @Override
public int mul(int i, int j) {
// TODO Auto-generated method stub
System.out.println("The method mul begins with[" + i + "," + j + "]");
int result = i * j;
System.out.println("The method mul ends with " + result);
return result;
} @Override
public int div(int i, int j) {
// TODO Auto-generated method stub
System.out.println("The method div begins with[" + i + "," + j + "]");
int result = i / j;
System.out.println("The method div ends with " + result);
return result;
} }

从上面代码可以看出,四个方法的日志有重复的部分。

Main.java

public class Main {

    public static void main(String[] args) {
// TODO Auto-generated method stub
ArithmeticCalculator arithmeticCalculator = null;
arithmeticCalculator = new ArithmeticCalculatorImpl(); int result = arithmeticCalculator.add(1, 2);
System.out.println("-->" + result); result = arithmeticCalculator.div(4, 2);
System.out.println("-->" + result);
} }

打印结果:

The method add begins with[1,2]
The method add ends with 3
-->3
The method div begins with[4,2]
The method div ends with 2
-->2

结果没什么问题,可是在加入日志后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还要兼顾其他多个关注点,并且在日志需求发生变化时,必须修改所有模块。

接下来,我们使用动态代理解决上述问题

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

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
public int add(int i, int j) {
// TODO Auto-generated method stub
int result = i + j;
return result;
} @Override
public int sub(int i, int j) {
// TODO Auto-generated method stub
int result = i - j;
return result;
} @Override
public int mul(int i, int j) {
// TODO Auto-generated method stub
int result = i * j;
return result;
} @Override
public int div(int i, int j) {
// TODO Auto-generated method stub
int result = i / j;
return result;
} }
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: 正在返回的那个代理对象,一般情况下,在invoke方法中都不使用对象
* method: 正在被调用的方法
* args: 调用方法时,传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
String methodName = method.getName();
//日志
System.out.println("The methods " + methodName + " begins with" + Arrays.asList(args));
//执行方法
Object result = method.invoke(target, args);
//日志
System.out.println("The methods " + methodName + " ends with" + result);
return result;
}
};
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}

Main.java

     ArithmeticCalculator target = new ArithmeticCalculatorImpl();
ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy(); int result = proxy.add(1, 2);
System.out.println("-->" + result); result = proxy.div(4, 2);
System.out.println("-->" + result);

打印结果:

The methods add begins with[1, 2]
The methods add ends with3
-->3
The methods div begins with[4, 2]
The methods div ends with2
-->2

用了动态代理方法,就会发现ArithmeticCalculatorImpl实现类变得清洁很多。并且日志被封装在invoke方法中,修改十分方便。

aop术语:

切面:横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知:切面必须要完成的工作

目标:被通知的对象

代理:向目标对象应用通知之后创建的对象

连接点:程序执行的某个特定位置,如类某个方法调用前,调用后,方法抛出异常后

由两个信息确定:方法表示的程序执行点,想对点表示的方位。

切点:每个类都拥有多个连接点,即连接点是程序类中客观存在的事务。

AOP通过切点定位到特定的连接点:类比:连接点相当于数据库中的记录,切点相当于查询条件。

前置通知:

Spring-aop(一)的更多相关文章

  1. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  2. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  3. spring aop

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...

  4. spring aop注解方式与xml方式配置

    注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...

  5. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  6. Spring AOP详解

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  7. Spring AOP实例——异常处理和记录程序执行时间

    实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...

  8. 从零开始学 Java - Spring AOP 实现用户权限验证

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...

  9. 从零开始学 Java - Spring AOP 实现主从读写分离

    深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...

  10. 从零开始学 Java - Spring AOP 拦截器的基本实现

    一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...

随机推荐

  1. YTU 2422: C语言习题 n个数逆序

    2422: C语言习题 n个数逆序 时间限制: 1 Sec  内存限制: 128 MB 提交: 150  解决: 96 题目描述 将n(n<20)个数按输入时顺序的逆序排列,用函数实现. 输入 ...

  2. OpenCV坐标系与操作像素的四种方法

    像素是图像的基本组成单位,熟悉了如何操作像素,就能更好的理解对图像的各种处理变换的实现方式了. 1.at方法 第一种操作像素的方法是使用"at",如一幅3通道的彩色图像image的 ...

  3. MyBatis学习 之 五、MyBatis配置文件

    在定义sqlSessionFactory时需要指定MyBatis主配置文件: <bean id="sqlSessionFactory" class="org.myb ...

  4. htm5 vido.js 播放器

    js统制html5 [video]标签中视频的播放和停止   需求:页面中有2个普通按钮a,b.还有一个video标签,能成功播放出视频..我想要的效果是,点击a按钮,视频开始播放,点击b按钮,视频播 ...

  5. css原生变量 var

    css 变量的支持情况: 在pc端支持还不错,ie不支持,移动端也不支持,大概这就是css变量没有广泛使用的原因吧 CSS预编译工具Sass/Less/Stylus的便捷之处就是可以定义变量,方便代码 ...

  6. 装饰器模式(Decorator) C++

    装饰器模式是比较常用的一种设计模式,Python中就内置了对于装饰器的支持. 具体来说,装饰器模式是用来给对象增加某些特性或者对被装饰对象进行某些修改. 如上图所示,需要被装饰的对象在最上方,它自身可 ...

  7. 使用git rebase合并多次commit

    使用git rebase合并多次commit 聊下 git rebase -i

  8. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(七) 学生信息增删

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  9. 关于TImer使用的注意

    晚点再写   停止Timer let timer = .... timer.invalidate()

  10. 最短路之Floyd(弗洛伊德)

    只有五行的Floyd最短路算法: 核心代码 每次都更新通过k点,然后从i到j的最短路程...