白话AOP术语

AOP作为OOP的一个强有力补充,发展到现在基本属于静态语言的标配了,君不见Java程序员谈及Spring,必提及IOC和AOP,只是相较于OOP中涉及到的专有名词,AOP相关的术语离我们日常生活比较遥远,诸如连接点(Joinpoint),切点(Pointcut),增强(Advice)等等,让第一次接触到的人难免有种丈二和尚摸不着头脑的感觉。

1. 概述

本文意图改变以往的先背诵相关术语,然后去结合实际例子,接着不断重复以上步骤以求理解的套路,转而直接从使用者的角度出发,由需求反推专有名词意义的方式来加快对AOP基础的理解。

2. 实例

假设有包含两个方法的一个类:

@Component
public class ClassBeAOP {
	public void doing() {
		System.out.println("doing somthing");
	}

	public void doing2() {
		System.out.println("doing somthing");
	}
}

现在我们的需求是要对其中的doing()方法进行一些强化,比如在方法执行前输出一段日志。(在正式开始论述AOP专有名称前,我将尽量避免它们的出现)。

正式开始前,先让我们来捋下上面需求的一些关键点:

  1. 进行强化的精确位置为ClassBeAOP类的doing()方法,其执行前。
  2. 用于强化原有业务代码的额外逻辑为 输出日志信息。

以上两条其实就是AOP相关基础术语的所要定义的全部内容,接下来就让我们结合AOP术语进行逐条分析:

2.1 切点(Pointcut)

关于对AOP生效位置的精确定位,我们需要将其拆分为两部分:编译时位置,运行时位置(注意这两个位置是笔者自己发明的,读者不要过于纠结,会在接下来进行解释)。

在本例中,编译位置为 ClassBeAOP类的doing()方法 , 而在AOP中被定义为 切点(Pointcut)

额外说明:

  1. 一个切点可以匹配多个连接点。
  2. 借用数据库的概念, 连接点为数据库里的记录, 而切点则是查询条件。
  3. AOP使用"切点"来定位特定的连接点。------ 也就是说Pointcut只能把连接点定位出来, 具体的方位还得额外提供,这也就是下面增强(Advice)中的内容了。
  4. 切点(Pointcut)在Spring可以通过注解@Pointcut()来声明。

2.2 增强(Advice)

对于作强化之用的额外代码,在AOP术语中就被称为 增强(Advice)

额外说明:

  1. 增强是织入目标类连接点上的一段程序代码。
  2. 在Spring中, 增强除了描述一段程序代码外, 还拥有另一个和连接点相关的信息, 这就是执行点的方位. 结合执行点的方位信息和切点信息, 就可以找到特定的链接。
  3. 所以增强既包括用于添加到目标连接点上的一段执行逻辑, 又包含用于定位连接点的方位信息.
  4. 因此Spring中提供的增强接口都是带有方位名的: BeforeAdvice(Before就是方位信息了), AfterReturningAdvice。
  5. 只有结合切点和增强,才能确定特定连接点并实施增强逻辑。------ 因为在Spring中,切点只是确定了执行点位置,还需要增强来提供方位和代表增强逻辑的程序代码。

2.3 连接点(Joinpoint)

以上我们一直在强调AOP切入点,那么势必存在一系列的备选点,而这些备选点在AOP术语中就被称为连接点(Joinpoint)

额外说明:

  1. 连接点由两个信息确定:
    • 执行点: 用方法表示程序执行点。
    • 方位: 用相对位置表示的方位。
    • 例如针对"Test.foo()执行前"这一段位置,其Joinpoint定义中,执行点为 Test.foo(), 方位为该方法执行前的位置。
  2. Spring使用切点对执行点进行定位,而方位则在增强类型中定义。
  3. 在Spring中,可单独使用 @Before()@After(),,@AfterReturning()@AfterThrowing(), @Around()来定义连接点。

2.4 切面(Aspect)

切面由切点和增强组成, 它既包括横切逻辑的定义, 也包括连接点的定义。

2.5 织入(Weaving)

织入是将增强添加到目标类的具体连接点上的过程。

一共有三种织入方式:

  1. 编译期织入, 需要特殊的Java编译器。
  2. 类装载期织入, 特殊的类加载器。
  3. 动态代理织入, 运行期为目标类添加增强生成子类。

以上,Spring采用3,而Aspect采用1和2。

2.6 其他术语

对于其他术语,这里我们直接一笔带过:

2.6.1 目标对象(Target)

增强逻辑的织入到的目标类。

2.6.2 引介(Introduction)

引介是一种特殊的增强, 其为类添加额外的属性和方法. 这样可以为业务类动态添加接口的实现, 让业务类成为该接口的实现类。

2.6.3 代理(Proxy)

一个类被AOP织入增强后, 产生的结果类就是糅合了原有逻辑和增强逻辑的代理类。

3. Spring AOP

以下是使用Spring的方式定义一个切面,对上面的ClassBeAOP进行AOP,以满足我们的需求。

@Component
@Aspect
public class ServiceAspect {

	// 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
	// 这里结合上面的讲解, @Pointcut只是确定了编译时位置, 我们还需要运行时位置, 也就是这里确定的方法的切入时机——是执行之前, 还是执行之后等等
	@Pointcut("execution(* xx.yy.zz.ClassBeAOP.doing(..))")
	public void aspect() {
	}

	// 配置前置通知,使用在方法aspect()上注册的切入点 同时接受JoinPoint切入点对象,可以没有该参数
	// 这里的 @Before 正是确定了运行时位置 --- 执行前
	@Before("aspect()")
	public void before(JoinPoint joinPoint) {
		// 这里的代码块就是 增强(Advice) 了。
		System.out.println("before!!!!!!!!");
	}
}

单元测试结果如下:

ClassBeAOP bean = bf.getBean("classBeAOP", ClassBeAOP.class);
/*
	输出
	  before!!!!!!!!
	  doing
*/
bean.doing();
/*
	输出
	  doing2
*/
bean.doing2();

4. 总结

最后我们结合Spring 来总结下AOP术语:

  1. 使用@Pointcut()(切点(Advice))来确定AOP生效时的编译时位置。
  2. 使用@Before()来确定AOP生效时的运行时位置。
  3. 有了以上两步就已经能够准确定位到织入位置了。
  4. 然后就是被@Before()修饰的方法里就是增强代码逻辑的增强(Advice)。
  5. 其实在Spring中可以省略@Pointcut(),而仅仅只需要借助@Before()即可同时完成编译时位置(@Before()中的 value() 值钱,)和运行时位置(@Before()代表的方法执行前),以及作为增强的@Before()修饰的方法体。

5. Links

  1. 《精通Spring4.x》P219

你可能感兴趣的