任何技术的出现都是为了解决某类问题。

AOP是什么?

AOP出现以前,代码逻辑都是从上到下编写。当我们需要在原有主逻辑不变的情况下,修改一些无关逻辑时,比如我们需要在每个方法中打印出入参,这时我们需要修改每个方法,然后加上打印日志的代码。这无疑是非常消耗精力的事情,而且这样的代码也不好维护,更不符合设计原则。

有没有更好的方式可以达成这样的目的?

我们可不可以在不修改原有逻辑的情况下将需要新增的代码插入到原有的代码中?

有的。我们可以创建原有对象的代理,将新增的代码写在代码对象的方法里,这样我们就可以不用修改原有对象的方法了。但是,方法这么多,难道我们要新增N 个代理对象吗?当然不是。我们有字节码工具,可以通过代码来为每个对象生成代理对象。

这样的局限性也很大。如果要在原有方法调用前插入日志需要新增代理对象,在调用后插入日志又要新增代理对象。随着需要插入的位置变多,我们就要创建更多的代理对象。

我们也可以通过在方法调用之间增加过滤器的方式,就像Servlet模式中的Filter一样。但是要给每个方法添加过滤器,显然不现实。

有没有一种更灵活的方式可以让我们控制插入的时机?

有。

AOP(Aspect Oriented Programming),面向切面编程。通过预编绎和运行期动态代理可以在不修改源代码的给程序添加功能。

在面向对象的世界里,我们将业务功能自顶而下地抽象为一个个的类,但是有一些功能频繁地出现在某些类中,从设计角度讲,这些功能不应该出现在这些类中,这时我们通过AOP技术将这些无关的功能抽象成切面,然后再指定这些功能往业务功能中织入的规则,最终整合到我们的系统中。

AOP 相关的概念

AOP本身只是一种思想,并不涉及到具体实现。为了解决上述问题,AOP引入了相关的基本概念。

Join Point

连接点,表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等。也就是我们要插入代码的地方。

Pointcut

连接点的集合。用来描述连接点。如通过通配符、正则表达式等语法来定义连接点。实际执行时代码是插入到某个具体的连接点。

Target Object

将被织入通知的对象。也被称作被通知对象。

AOP proxy

AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

Advice

定义具体要插入的内容,以及在连接点插入的时机,通过beforeafter等语义关键词表示。

插入时机

也可以理解为通知的类型。Spring中可以分为以下几种:

  1. Before

    前置通知,在连接点前调用。

  2. After

    后置通知,在连接点后调用。

  3. AfterReturning

    返回通知,在连接点方法执行并正常返回后调用

  4. AfterThrowing

    异常通知,当连接点方法异常时调用

  5. Around

    环绕通知

Introduction

引入。添加方法或字段到被通知的类。 Spring允许引入新的接口或者字段到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。

Aspect

切面。连接点,切点,通知的载体。由这三者组合起来,才能称为一个完整的切面。

面向切面编程,也即是将我们想要新增的不影响主逻辑的代码抽出,并将其插入到原有目标代码的过程。

AOP应用场景

在Spring中应用最广泛地当属事务管理。将事务控制抽象出切面,交由Spring IOC容器帮我们织入到目标对象中,并管理这些目标对象。想象一下如果没有AOP,那我们需要频繁地开启、提交、回滚事务。

还有日志记录,权限验证,链路追踪等。