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. 第八周学习总结&实验报告六

    实验总结 :类的继承 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法. 实验要求 : 练习捕获异常.声明异常.抛出异常的方法.熟悉try和catch子句的使用. 掌握自定义异 ...

  2. 《统计学习方法(李航)》讲义 第03章 k近邻法

    k 近邻法(k-nearest neighbor,k-NN) 是一种基本分类与回归方法.本书只讨论分类问题中的k近邻法.k近邻法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类 ...

  3. c# 单元测试 ,对静态方法(static)和私有方法(private) 进行单元测试

    利用反射: /// <summary> /// 调用静态方法 /// </summary>akf /// <param name="t">类全名 ...

  4. vue 钩子函数中获取不到DOM节点

    原文链接:https://jingyan.baidu.com/article/f96699bbfe9c9d894f3c1b4b.html 两种解决方案: 1:官方解决方案: 受到 HTML 本身的一些 ...

  5. leetcode 496下一个更大的元素I

    单调递减栈来做,time O(n),spaceO(n)需要一个哈希map class Solution { public: vector<int> nextGreaterElement(v ...

  6. unity3d卡在loading的解决方法

    解决方案: 关闭U3D,打开电脑的网络适配器把网给掐掉,打开U3D后会提示Workoffline 点击这个即可,然后再把网给起起来就可以了

  7. kotlin之字符串

    在kotlin 中 使用String 表示字符串类型,如下二类字符串 一 普通字符串 var s1 = "hello world" 二 保留原始格式的字符串 var s2 = &q ...

  8. Solr安装并导入mysql数据

    参考博客:https://blog.csdn.net/u013160017/article/details/81037279 下载地址:https://lucene.apache.org/solr/m ...

  9. template要加s,重启服务,不然报错

  10. ABAP开发常用函数

    1.  LAST_DAY_OF_MONTHS——用于获取某月的最后一天. 例如输日今天的日期2013/10/16,系统将回执成2013年10月的最后一天,也就是2013/10/30 2.  CONVE ...