面向切面编程(AOP)

面向切面编程(AOP)

面向切面编程(Aspect Oriented Programming,缩写为AOP)是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。

在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
面向切面编程(AOP)
面向切面编程是一种将横切关注点与业务逻辑分离的编程方式。每个横切关注点都集中在一个地方,而不是分散在多处代码中。这样使我们的服务模块更加简洁,因为它们只包含了主要关注点的代码,而次要的功能或者说辅助的功能被转移到切面中了。
面向切面编程(AOP)
上图表示划分为三个服务模块的应用,每个模块提供相应的服务,而且这些模块都需要类似的辅助功能,例如日志、安全、事务等等。我们并不想在各个模块中写重复的日志、安全、事务的代码,那么就可以使用选用切面这个方案,来解决这个问题。

主要功能

日志记录,性能统计,安全控制,事务处理,异常处理等等。

主要意图

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

AOP和OOP

区分
AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
上面的陈述可能过于理论化,举个简单的例子,对于“雇员”这样一个业务实体进行封装,自然是OOP/OOD的任务,我们可以为其建立一个“Employee”类,并将“雇员”相关的属性和行为封装其中。而用AOP设计思想对“雇员”进行封装将无从谈起。
同样,对于“权限检查”这一动作片断进行划分,则是AOP的目标领域。而通过OOD/OOP对一个动作进行封装,则有点不伦不类。
换而言之,OOD/OOP面向名词领域,AOP面向动词领域。

关系
很多人在初次接触 AOP 的时候可能会说,AOP 能做到的,一个定义良好的 OOP 的接口也一样能够做到,我想这个观点是值得商榷的。AOP和定义良好的 OOP 的接口可以说都是用来解决并且实现需求中的横切问题的方法。但是对于 OOP 中的接口来说,它仍然需要我们在相应的模块中去调用该接口中相关的方法,这是 OOP 所无法避免的,并且一旦接口不得不进行修改的时候,所有事情会变得一团糟;AOP 则不会这样,你只需要修改相应的 Aspect,再重新编织(weave)即可。 当然,AOP 也绝对不会代替 OOP。核心的需求仍然会由 OOP 来加以实现,而 AOP 将会和 OOP 整合起来,以此之长,补彼之短。

AOP 术语

面向切面编程(AOP)
advice - 通知
切面的具体行为,即要切入执行的代码

pointcut - 切点
通知被应用的具体位置

join point - 连接点
程序运行时,能够应用通知的所有点

aspect - 切面
什么时候在什么地方做什么事情,是切点和通知的结合

target - 目标对象
被切入功能的目标对象

introduction - 引入
将新的方法或属性引入到现有的类中

weaving - 织入
把切面应用到目标对象并创建新的代理对象的过程

代理模式

代理模式是使用代理对象完成用户请求,屏蔽用户对真实对象访问的一种设计模式。现实生活中,代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角度看,他只和代理人通信。而事实上代理人是要有当事人的授权,并且在核心问题上还需要请示当事人。

AOP 就是使用代理模式实现的,其中的代理类就相当于AOP中的切面。
面向切面编程(AOP)
静态代理
之所以称为静态代理,是因为在程序运行前,代理类就已经存在了。

动态代理
动态代理是在程序运行时,利用Java反射机制动态的生成代理类的代理模式。

JDK动态代理
JDK的动态代理依靠接口实现
如果类并没有实现接口,则不能使用Jdk的动态代理

CGLIB动态代理
JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这时就要使用cglib动态代理了。

Spring AOP

Spring 提供了4中 aop 的支持,基于代理的经典SpringAOP,纯POJO切面,@Aspect注解驱动的切面,注入式AspectJ切面。前三种都是SpringAOP实现的变体,SpringAOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。第四种实际是使用AspectJ的解决方案。提供了SpringAOP所不能支持的许多类型的切点。我们可以借助Spring的依赖注入把bean装配进AspectJ切面中。Spring借鉴了AspectJ的切面,以提供注解驱动的AOP。本质上,它依然是Spring基于代理的AOP。

使用@Aspect创建切面类
Audience类使用@Aspect注解进行了标注。该注解表明Audience不仅仅是一个POJO,还是一个切面。Audience类中的方法都使用注解来定义切面的具体行为。Audience有四个方法,定义了一个观众在观看演出时可能会做的事情。在演出之前,观众要就坐(takeSeats())并将手机调至静音状态(silenceCellPhones())。如果演出很精彩的话,观众应该会鼓掌喝彩(applause())。不过,如果演出没有达到观众预期的话,观众会要求退款(demandRefund())。
面向切面编程(AOP)
面向切面编程(AOP)
Advice的5种注解
面向切面编程(AOP)
使用@Pointcut声明切点表达式
@Pointcut 声明使用频繁的切点表达式后,在通知注解中引用即可
面向切面编程(AOP)

总结

  • 面向切面编程是面向对象编程的一个强大的补充

  • 通过切面可以把分散在应用各处的行为放入可重用的模块中

  • 通过注解显示的声明在什么地方应用该行为

  • 有效减少冗余代码,让我们的类关注自身的主要功能

  • 动态代理模式和静态代理模式

  • 通过使用@Aspect注解和简单的配置,可以很简单的在Spring中装配advice和pointcut

参考文章:
https://baike.baidu.com/item/AOP/1332219?fr=aladdin
https://www.jianshu.com/p/be6cb39dbeb7