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基础的更多相关文章

  1. Spring笔记:AOP基础

    Spring笔记:AOP基础 AOP 引入AOP 面向对象的开发过程中,我们对软件开发进行抽象.分割成各个模块或对象.例如,我们对API抽象成三个模块,Controller.Service.Comma ...

  2. Spring IOC和AOP 基础

    1 spring中注入资源是通过描述来实现的,在 spring 中是通过注解或者 XML 描述.spring 中IOC 注入方式有三种 1)构造方法注入 2)setter 注入 3)接口注入 1.1) ...

  3. 【AOP】Spring AOP基础 + 实践 完整记录

    Spring AOP的基础概念 ============================================================= AOP(Aspect-Oriented Pr ...

  4. [Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

    前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习 ...

  5. [Spring框架]Spring AOP基础入门总结一.

    前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect ...

  6. CgLib动态代理学习【Spring AOP基础之一】

    如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...

  7. Spring基础系列--AOP织入逻辑跟踪

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9619910.html 其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置.后 ...

  8. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

  9. 15Spring AOP基础

    为什么需要AOP? 先来看一段代码: package com.cn.spring.aop.helloworld; //加减乘除的接口类 public interface ArithmeticCalcu ...

随机推荐

  1. 套接字之select系统调用

    select是IO多路复用的一种方式,用来等待一个列表中的多个描述符的可读可写状态: SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_ ...

  2. loggin模块,错误日志模块

    # 记录用户行为或者报错信息 import logging # 配置错误日志有两种方法 # 方法1:通过basicconfig # 配置简单.但是能做的事情少,解决不了中文乱码,不能同时输出到屏幕和文 ...

  3. 代码实现:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%; 利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%; 20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%; 60万到100万之间时,高于60万元的部分,可提成1.5%,高于100万元时,超过100万元

    import java.util.Scanner; /* 企业发放的奖金根据利润提成.利润(I)低于或等于10万元时,奖金可提10%: 利润高于10万元,低于20万元时,低于10万元的部分按10%提成 ...

  4. [VBA]汇总多个工作簿的指定工作表到同一个工作簿的指定工作表中

    sub 汇总多个工作簿() Application.ScreenUpdating = False Dim wb As Workbook, f As String, l As String, n As ...

  5. ControlTemplate in WPF —— Window

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" x ...

  6. web开发(九) 使用javamail进行发送邮件,(使用QQ,163,新浪邮箱服务器)

    在网上看见一篇不错的文章,写的详细. 以下内容引用那篇博文.转载于<http://www.cnblogs.com/whgk/p/6506027.html>,在此仅供学习参考之用. 一.发送 ...

  7. git clone的代码没有前端代码

    需要切换到文件夹目录下,然后 git checkout dev 切换到开发环境(其实是切换到了dev分支) 后来查看分支,发现有两个分支:master 和 dev . ================ ...

  8. gitlab中的CI

    https://blog.csdn.net/chengzi_comm/article/details/78778284

  9. JavaScript Source Maps浅析

    阅读目录 有用的链接 Link: 原文链接 译文开始: 对网站进行性能优化对一个最容易的方法就是把JS和CSS进行打包压缩.但是当你需要调试这些压缩文件中的代码的时候,会发生什么?可能会是一场噩梦.但 ...

  10. js在页面中添加一个元素 —— 添加弹幕

    参考地址 [往下拉 —— 使用HTML DOM appendChild() 方法实现元素的添加 ] 一.创建 HTML <div class="right_liuyan"&g ...