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. android gradle,groovy--https://blog.csdn.net/hebbely/article/details/79074460

    android grale,groovyhttps://blog.csdn.net/hebbely/article/details/79074460 Gradle编译时报错:gradle:peer n ...

  2. 1. 参数的传入和添加 argparse.ArgumentParser()

    # Edit configuration 传入的参数使用的是--file_dir picture, 获取使用的是argv.file_dir import argparse, sys def parse ...

  3. OpenCV学习笔记(8)——图像平滑

    使用不同的低筒滤波器对图像进行模糊 使用自定义的率弄起对图像进行卷积(2D卷积) 2D卷积 与信号一样,我们也可以对2D图像实施低通滤波,高通滤波等.LPF帮助我们去除噪声,模糊图像.而HPF帮助我们 ...

  4. linux 代码更新-打包-重启脚本

    #! /bin/sh base=/home/project/myblog cd $base git pull ] then echo "Error in git pull!!! Stop d ...

  5. 【SVN】更新提交失败---- Previous operation has not finished; run 'cleanup' if it was interrupted解决方法

     Previous operation has not finished; run 'cleanup' if it was interrupted 问题出处 解决方法 2017-11-01   08: ...

  6. CTF—WEB—sql注入之宽字节注入

     宽字节注入 宽字节注入是利用mysql的一个特性,mysql在使用GBK编码(GBK就是常说的宽字节之一,实际上只有两字节)的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字 ...

  7. HTML DOM Document对象 元素对象 属性对象 事件对象

    DOM Document对象 DOM 元素 对象 DOM 属性 对象 DOM 事件 菜鸟教程上 总结挺全的,就不多废话,链接点进去即可.. 后期对经常用到的会在此更新一些总结..... 开学了...自 ...

  8. Linux系统基本操作

      学习目标: 通过本实验掌握Linux的系统安装.磁盘分区.文件管理.IP地址配置.SSH远程管理.WinSCP文件传输.chmod文件权限管理.tar及gzip/bzip2压缩工具的使用,以及关机 ...

  9. PEP8-python编码规范(上)

    包含主要 Python 发行版中的标准库的 Python 代码的编码约定. 1.代码缩进 (1)每个缩进需要使用 4 个空格.一般使用一个Tab键. Python 3 不允许混合使用制表符和空格来缩进 ...

  10. 第七次学习总结&&第五次实验报告

    一.实验目的 (1)理解抽象类与接口的使用: (2)了解包的作用,掌握包的设计方法. 二.实验要求 (1)掌握使用抽象类的方法. (2)掌握使用系统接口的技术和创建自定义接口的方法. (3)了解 Ja ...