Spring基础20——AOP基础
1、什么是AOP
AOP(Aspect-Oriented Programming)即面向切面编程,是一种新的方法论,是对那个传统OOP面向对象编程的补充。AOP的主要编程对象是切面(aspect),而切面模块化横切关注点,在应用AOP编程时,仍需要定义公共功能但可以明确功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就模块化到了特殊得对象(切面)里,那么什么是面向切面编程呢?
下面我们通过一个例子来理解:
我们需要实现一个计算器,并且有以下两个需求:
需求1:日志,在程序执行期间追寻正在发生的活动。
需求2:希望计算器只能处理正数的运算。
首先我们来定义一个接口,来描述计算器的功能:
public interface ArithmeticCalculator {
/**
* 加法
* @param i
* @param j
* @return
*/
int add(int i, int j);
/**
* 减法
* @param i
* @param j
* @return
*/
int sub(int i, int j);
/**
* 乘法
* @param i
* @param j
* @return
*/
int mul(int i, int j);
/**
* 除法
* @param i
* @param j
* @return
*/
int div(int i, int j);
}
之后我们实现这个接口并对方法进行实现:
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;
}
}
编写Main测试类对功能进行测试:
public class Main {
public static void main(String[] args) {
ArithmeticCalculator calculator = new ArithmeticCalculatorImpl();
System.out.println(calculator.add(1,2));
System.out.println(calculator.sub(2,1));
System.out.println(calculator.div(4,2));
System.out.println(calculator.mul(2,4));
}
}
我们可以观察到输出结果是正常的。

在上面的基础上,修改ArithmeticCalculatorImpl 我们下面来完成上面两个需求:
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
if (i < 0 ||j < 0 ) {
System.out.println("操作数必须为正数");
return -1;
}
System.out.println("The add method begin [ " + i + ", " + j + " ]" );
int result = i + j;
System.out.println("The add method end [ " + result + " ]" );
return result;
}
@Override
public int sub(int i, int j) {
if (i < 0 ||j < 0 ) {
System.out.println("操作数必须为正数");
return -1;
}
System.out.println("The sub method begin [ " + i + ", " + j + " ]" );
int result = i - j;
System.out.println("The sub method end [ " + result + " ]" );
return result;
}
@Override
public int mul(int i, int j) {
if (i < 0 ||j < 0 ) {
System.out.println("操作数必须为正数");
return -1;
}
System.out.println("The mul method begin [ " + i + ", " + j + " ]" );
int result = i * j;
System.out.println("The mul method end [ " + result + " ]" );
return result;
}
@Override
public int div(int i, int j) {
if (i < 0 ||j < 0 ) {
System.out.println("操作数必须为正数");
return -1;
}
System.out.println("The div method begin [ " + i + ", " + j + " ]" );
int result = i / j;
System.out.println("The div method end [ " + result + " ]" );
return result;
}
}
再次运行测试类:
public class Main {
public static void main(String[] args) {
ArithmeticCalculator calculator = new ArithmeticCalculatorImpl();
System.out.println(calculator.add(-1,2));
System.out.println(calculator.sub(2,1));
System.out.println(calculator.div(4,2));
System.out.println(calculator.mul(2,4));
}
}
输出结果:我们看到是实现了之前的需求。

2.使用动态代理对程序进行改进
我们用上面的代码实现了之前的两个需求,但是我们可以观察到,每个方法中都包含大量的重复代码,如果一旦需要修改日志或参数校验的规则,我们每个方法都要去修改,这显然是非常不利于维护的,那么我们可以采用——动态代理的方式来进行改进。代理模式原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象任何原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。

代理类ArithmeticCalculatorLoggingProxy:
public class ArithmeticCalculatorLoggingProxy {
private ArithmeticCalculator calculator;
public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator calculator) {
this.calculator = calculator;
}
public ArithmeticCalculator getCalculatorProxy() {
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
System.out.println("The method " + method.getName() + " begin with " + Arrays.asList(args));
Object result = null;
try {
//前置通知
result = method.invoke(calculator, args);
//返回通知,可以访问到方法的返回值
} catch (Exception e) {
e.printStackTrace();
//异常通知,可以访问到方法出现的异常
}
//后置通知,方法可能会出异常,所以访问不到方法的返回值
System.out.println("The method add " + method.getName() +" with " + result);
return result;
}
};
return (ArithmeticCalculator) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), calculator.getClass().getInterfaces(), handler);
}
}
将ArithmeticCalculatorImpl类改到最初的版本,重新运行测试类,我们可以得到同样的结果

3.AOP中的一些概念及AOP的好处
AOP中的一些概念:

切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象,如上图日志切面和验证切面(在后面还会讲spring aop切面的优先级)。
通知(Advice):切面必须要完成的工作。(切面里的每一个方法,比如前置日志和后置日志)
目标(Target):被通知的对象(被代理对象)
代理(Proxy):向目标对象应用通知之后创建的对象(代理对象)
连接点(JoinPoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示程序执行点;相对点表示的方位。例如ArithmethicCalculator#add()方法执行前的连接点执行点为ArithmethicCalculator#add()方位为该方法执行前位置。
切点(Pointcut):每个类都拥有多个连接点:例如ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序中客观存在的事物,AOP通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一一对应的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。
AOP的好处:
每个事物逻辑位于一个位置,代码不分散,便于维护和升级
业务模块更加简洁只包含核心业务代码。
Spring基础20——AOP基础的更多相关文章
- Spring笔记:AOP基础
Spring笔记:AOP基础 AOP 引入AOP 面向对象的开发过程中,我们对软件开发进行抽象.分割成各个模块或对象.例如,我们对API抽象成三个模块,Controller.Service.Comma ...
- Spring IOC和AOP 基础
1 spring中注入资源是通过描述来实现的,在 spring 中是通过注解或者 XML 描述.spring 中IOC 注入方式有三种 1)构造方法注入 2)setter 注入 3)接口注入 1.1) ...
- 【AOP】Spring AOP基础 + 实践 完整记录
Spring AOP的基础概念 ============================================================= AOP(Aspect-Oriented Pr ...
- [Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.
前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习 ...
- [Spring框架]Spring AOP基础入门总结一.
前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect ...
- CgLib动态代理学习【Spring AOP基础之一】
如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...
- Spring基础系列--AOP织入逻辑跟踪
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9619910.html 其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置.后 ...
- Spring基础系列-AOP源码分析
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...
- 15Spring AOP基础
为什么需要AOP? 先来看一段代码: package com.cn.spring.aop.helloworld; //加减乘除的接口类 public interface ArithmeticCalcu ...
随机推荐
- 界面之下:还原真实的 MV* 模式
界面之下:还原真实的MV*模式 作者:戴嘉华 转载请注明出处并保留原文链接( https://github.com/livoras/blog/issues/11 )和作者信息. 目录: 前言 MVC ...
- spark 笔记 8: Stage
Stage 是一组独立的任务,他们在一个job中执行相同的功能(function),功能的划分是以shuffle为边界的.DAG调度器以拓扑顺序执行同一个Stage中的task. /** * A st ...
- awk命令2
提取文件后四行 注释:NR==FNR表示第一个文件,执行{a++},计算出第一个文件10的行数,NR!=FNR表示第二个文件10,执行{if(FNR<=a-4){print $0}},打印出第二 ...
- Android共享元素场景切换动画的实现
安卓5.0系统引入了共享元素,能做出非常炫酷的场景切换效果,这让人非常兴奋同时非常蛋疼,因为低版本没法使用啊,所以今天就跟大家分享一下自己写的一个库,其实只有2个文件而已就可以兼容安卓5.0以下的版本 ...
- ListView 如何提高其效率?
① 复用 ConvertView② 自定义静态类 ViewHolder③ 使用分页加载 ④ 使用 WeakRefrence 引用 ImageView 对象 ViewHolder 为什么要声明为静态类? ...
- Go语言引用类型
切片 1.切片定义 a) 声明一个切片 , , } , , } b) 通过make来创建切片 ) c) 通过 := 语法来定义切片 slice := []int{} slice := make([], ...
- oracle数据库面试相关
1.实现分页 rownum: select * from (select aa.* rownum rn from (select * from student )aa where rownum < ...
- 原生dapper中新增用户后根据用户id,在用户角色表中添加关联数据,事务处理
var result = 0; var userId = 0; using (var db = _Sql.Connection) using (var tran =db.BeginTransactio ...
- 【HTML5】---【HTML5提供的一些新的标签用法以及和HTML 4的区别】
HTML 5 是一个新的网络标准,目标在于取代现有的 HTML 4.01, XHTML 1.0 and DOM Level 2 HTML 标准.它希望能够减少浏览器对于需要插件的丰富性网络应用服务(p ...
- tagged和untagged
tagged和untagged遵循以下五条原则 1. Tagged数据帧 Tagged数据帧 Untagged数据帧 Untagged数据帧 in out in out Tagged端口 原样 ...