一、AOP——另一种编程思想
1.1 什么是 AOP
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
从《Spring实战(第4版)》图书中扒了一张图:
从该图可以很形象地看出,所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
1.2 为什么需要 AOP
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
1.3 AOP 实现分类
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,看到这其实应该明白了,AOP 其实就是前面一篇文章讲的代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:
- 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
- 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。
下面给出常用 AOP 实现比较
二、AOP 术语
AOP 领域中的特性术语:
- 通知(Advice): 就是我们编写的希望Aop时执行的那个方法。我们通过Aop希望我们编写的方法在目标方法执行前执行,或者执行后执行。
- 连接点(join point): 连接点可以说是切点的全集。切点是连接点的子集。也可以理解为,连接点是我们没有定义那个select开头规则时,满足条件的全部的方法。
- 切点(PointCut): 切点就是我们配置的满足我们条件的目标方法。比如我们规定:名字前面是select开头的才执行我们自定义的通知方法。那么这些select开头的方法就是切点。
- 切面(Aspect): 切面是切点和通知的组合称谓,就是变相给组合起了个名。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
-
public class BuyAspectJ { //切点:com.sharpcj.aopdemo.test1.IBuy.buy @Before("execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))") //通知:haha public void haha(){ System.out.println("男孩女孩都买自己喜欢的东西"); } }
概念看起来总是有点懵,并且上述术语,不同的参考书籍上翻译还不一样,所以需要慢慢在应用中理解。
三、初步认识 Spring AOP
3.1 Spring AOP 的特点
AOP 框架有很多种,1.3节中介绍了 AOP 框架的实现方式有可能不同, Spring 中的 AOP 是通过动态代理实现的。不同的 AOP 框架支持的连接点也有所区别,例如,AspectJ 和 JBoss,除了支持方法切点,它们还支持字段和构造器的连接点。而 Spring AOP 不能拦截对对象字段的修改,也不支持构造器连接点,我们无法在 Bean 创建时应用通知。
3.2 Spring AOP 的简单例子
下面先上代码,对着代码说比较好说,看下面这个例子:
这个例子是基于gradle创建的,首先 build.gradle 文件添加依赖:
dependencies {
compile 'org.springframework:spring-context:5.0.6.RELEASE'
}
首先创建一个接口 IBuy.java
package com.sharpcj.aopdemo.test1;
public interface IBuy {
String buy();
}
Boy 和 Gril 两个类分别实现了这个接口:
Boy.java
package com.sharpcj.aopdemo.test1;
import org.springframework.stereotype.Component;
@Component
public class Boy implements IBuy {
@Override
public String buy() {
System.out.println("男孩买了一个游戏机");
return "游戏机";
}
}
Girl.java
package com.sharpcj.aopdemo.test1;
import org.springframework.stereotype.Component;
@Component
public class Girl implements IBuy {
@Override
public String buy() {
System.out.println("女孩买了一件漂亮的衣服");
return "衣服";
}
}
配置文件, AppConfig.java,用于开启Spring Aop功能
package com.sharpcj.aopdemo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Confi