Spring AOP 从使用入门走近源码

发表于 2022-12-15

前言

我们从早期使用 AOP 的方式入手,走入 Spring AOP 源码。都说想要提升技术能力,多看看源码有利于提高自己的编码能力,事实也是如此。源码学习存在一些问题,枯燥无味还容易钻牛角尖。我们换个角度,从早期使用 Spring AOP 的方式入手,先了解 Spring AOP 的概貌。

先简单说一下常规使用到 Spring AOP 早期的使用方式,再讲解一些概念。这样从最简单最熟悉的地方入手,逐步深入学习,避免直入源码的茫然无措。

Spring AOP 的使用

我们常规的使用 Spring AOP 都是通过注解的方式,定义切面、切点和通知。往前推一点还有 XML 配置,再往前就是通过实现接口配置切面的方式。后面主要说明最常用的注解形式,还有就是贴近源码的接口形式。

注解的方式

先直接上代码,从具体到抽象。定义一个目标 Bean,是我们 AOP 需要织入切面的目标。

@Component
public class TargetBean {
        // 目标方法
	public String message() {
		return "TargetBean Message";
	}
}

紧接着就定义切面,并再切面中声明通知和切点。

// 定义切面
@Aspect
@Component
public class AnnotationAop {
	// 定义切点(AspectJ 表达式)
	@Pointcut("execution(* com.devnolo.demo.spring.aop.target.TargetBean.message())")
	public void pointCut() {
	};
	// 定义通知,使用切点表达式
	@Before(value = "pointCut()")
	public void aopBefore() {
		System.out.println("AnnotationAop AopBefore");
	}
}

一个切面就定义完成并且成功织入业务代码中了,到这里应该都是大家所容易理解,使用注解的方式实现 Spring AOP。

实现 Spring 接口的方式

在贴上代码之前先说几个概念,然后再结合两种实现的代码就能很容易理解 Spring AOP 的基础概念了。接下来我们将认识到这几个概念:通知(Advice)、切点(Point Cut)、切面(Aspect)、顾问(Advisor)

什么是通知?通知在我的理解中是一段具体的代码,是我们所期望在业务代码前后执行的逻辑。

什么是切点?切点更多的是一段规则,我们是期望在某某方法的执行前执行我们的通知。

什么是切面?切面是一个抽象的概念,是一个完整的行为。例如我们需要在某某方法的执行前记录日志这是一个切面,这个切面包含两部分:记录日志的通知,确定执行位置的切点。

什么是顾问?顾问可以看出切面的实现,顾问通常承载了一个切面和一个切点(一段匹配规则)。所以顾问可以完成执行位置的匹配和逻辑代码的执行。

这里举一个通过实现 Spring 接口实现 AOP 的例子。

先定义一个通知(Advice)

// 实现 MethodBeforeAdvice 方法前置通知接口,并完成方法实现
public class AdviceAop implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("AopAdvice AopBefore");
	}
}

在定义一个顾问(Advisor),由名字 NameMatchMethodPointcutAdvisor 可以看出这个顾问的匹配规则是通过方法名称进行匹配的,同样还有其他类型,例如正则。

@Configuration
public class AdvisorConfiguration {
	@Bean
	public NameMatchMethodPointcutAdvisor aspectAdvisor() {
		// 顾问(Advisor、通知者):是经过包装后的细粒度控制方式(带了切点)
		NameMatchMethodPointcutAdvisor advisor=new NameMatchMethodPointcutAdvisor();
		// 通知(Advice) :是我们的通知类,就是一段逻辑代码
		advisor.setAdvice(new AdviceAop());
		// 声明切点规则(这里是匹配方法名称)
		advisor.setMappedNames("message");
		return advisor;
	}
}

有一个稍微特殊的通知是环绕通知,它的接口定义是:MethodInterceptor,同其他前置后置这类的命名规则有所差异。

概念

概念其实在说接口实现 AOP 时已经说过了,我觉得在那里讲解更容易理解。在这里在做一次总结,也简单描述一下其他概念。

  • 通知

    • 是我们所期望在业务代码前后执行的逻辑。例如我们会在这里记录日志。
  • 切点

    • 是一段规则。例如我们期望在某某方法的执行前执行,这个逻辑就是规则(切点)。
  • 切面

    • 切面是一个抽象的概念,是一个完整的行为。例如我们需要在某某方法的执行前记录日志这是一个切面,这个切面包含两部分:记录日志的通知,确定执行位置的切点。
  • 顾问

    • 顾问可以看出切面的实现,顾问通常承载了一个切面和一个切点(一段匹配规则)。所以顾问可以完成执行位置的匹配和逻辑代码的执行。
  • 连接点(JointPoint)

    • 一个具体的位置。同样举例说明,我们希望在方法名叫 aMethod 前执行,那么 ClassAaMethod 方法这就是一个连接点,ClassBaMethod 也是一个切入点。
  • 目标(Target)

    • 我们的 Bean
  • 代理(Proxy)

    • 目标被织入通知后的增强 Bean

最后贴一张图,期望帮助理解。

AOP

总结

其实理解通过实现 Spring 接口的方式实现 AOP,以及 AOP 的相关概念的时候,已经算是理解了 Spring AOP 的基本实现,这里再做一个整合总结。

在使用 Spring AOP 的注解时,最后也会被封装成一个个的顾问(Advisor)。所以当理解接口的实现逻辑时,对于 Spring AOP 源码也有了基本认识,最近在学习 Spring 源码,有收货后再整理后续内容。