Spring源码浅析之AOP、Aspect、Advice

  • Post author:
  • Post category:其他




前言

理一理AOP与切面(Aspect)、通知(Advice)的关系



概念

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。

  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。

  • 切点(PointCut): 可以插入增强处理的连接点。

  • 切面(Aspect): 切面是通知和切点的结合。

    在这里插入图片描述

    做了张图

    在这里插入图片描述

  • PointCut:指的是具体要被切的方法

  • JoinPoint:指的是切点的哪里要被切,执行前就是@before,执行后就是@after

  • Advice: 每个JoinPoint对应一个Advice,具体执行的方法就称为Advice

  • Aspect: JoinPoint+Advice组成了一个切面类,如代码定义的那样



实现

  • 这部分主要讲一下AOP,AOP就是Aspect Oriented Programming(面向切面编程),主要通过上述几个组件实现的,那么具体源码层次是怎么实现的呢?
  • 在之前的文章中,我对AOP进行了一次探究,并没有进行总结,在这里做一下总结



流程图

看完源码我喜欢画一张流程图,AOP也不例外

在这里插入图片描述

黄色部分(后置处理器注入)

  • @EnableAspectJAutoProxy开启AOP
  • 其中包括了@Import(AspectJAutoProxyRegistrar.class),熟悉IOC原理的知道,Spring会将@Import注解导入的类导入容器中
  • AspectJAutoProxyRegistrar 是一个ImportBeanDefinitionRegistrar,因此作用就是进行Bean组件的注册
  • 其调用registerBeanDefinitions最终将AnnotationAwareAspectJAutoProxyCreator注册到容器中
  • 而AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator
  • AbstractAutoProxyCreator是一个BeanPostProcessor
  • 至此,与AOP相关的处理器注册完成

红色部分(初始化Bean)

  • 这部分是简略版的IOC初始化流程
  • 切点对应的代理对象这时候就会被创建
  • 主要是在initializeBean时,执行了applyBeanPostProcessorsAfterInitialization
  • 即调用对应BeanPostProcessor的postProcessAfterInitialization方法,与AOP相关的BeanPostProcessor就是黄色部分注入的AbstractAutoProxyCreator

蓝色部分(生成代理对象)

  • 在AbstractAutoProxyCreator的postProcessAfterInitialization方法中
  • 会先通过getAdvicesAndAdvisorsForBean获取Bean对应的Advice
  • 这里的Advice就是在Spring扫描定义好的@Aspect切面类时保存好的,具体Advice扫描过程在此不展开,上述截图例子中,Advice就对应那三个方法
  • 接着,将相关的advice作为参数,传到createProxy方法中,这一步通过CGlib为Bean生成一个代理对象,并且其中保存了advice

最终就能返回一个代理Bean,并将其注入容器中

至于代理对象执行对应方法时,advice的调用过程这里就不分析了,前一篇AOP源码解析中有讲

简单的说就是,遍历advice,不同连接点对应的advice实现的接口不同,通过接口类型的判断,得出不同的advice执行的时间,进而先执行advice,就能实现AOP的功能了

在这里插入图片描述



Advice

由于Advice在后面理解事务底层实现上要用到,在此进一步进行分析

上面也提到了,不同连接点对应的Advice实现的接口类型不同,我们通过debug可以观察到上述例子在生成代理对象时会创建哪些advice

在这里插入图片描述

一次点开,可以发现分别为

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

也就意味着,如果我们要自定义Advice,并让其起作用的话,我们只需要实现相关连接点的接口,并且能让AOP在创建代理对象时扫描到,即可实现自定义,这部分在事务解析时会提及



适配器模式

我们发现,在创建代理对象时,涉及了一个Interceptor,四个advice,调用代理对象方法时,获取到的调用链chain确是三个Interceptor,2个advice,为什么?

首先,调用Cglib代理对象方法时,会调用intercept方法,其中就会完成方法链的调用,如下所示

在这里插入图片描述

我们可以看看proceed()做了什么,如下图所示,可以看到其实主要是调用invoke方法,并且invoke方法是MethodInterceptor接口定义的

在这里插入图片描述

也就意味着,如果advice要在代理方法被调用时执行,那必须是MethodInterceptor接口的

那么我们看看在创建代理对象时,绑定的四个adivce有没有实现MethodInterceptor接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可以看到四个中有两个实现了MethodInterceptor接口,分别为AspectJAfterThrowingAdvice、AspectJAfterAdvice,这两个advice最后也原封不动地在chain调用链中出现,那么其他两个怎么办呢?

为了将没有实现MethodInterceptor接口的advice实现接口,就需要适配器模式了

对应的适配器接口为AdvisorAdapter,其定义了两个方法

public interface AdvisorAdapter {

	boolean supportsAdvice(Advice advice);
	
	MethodInterceptor getInterceptor(Advisor advisor);

}

可以看到只需要调用AdvisorAdapter的getInterceptor,传入一个advisor,就能够将其适配为MethodInterceptor

具体看看其中一个实现 MethodBeforeAdviceAdapter,getInterceptor返回的是MethodBeforeAdviceInterceptor

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}

}

MethodBeforeAdviceInterceptor相当于在调用invoke的时候,执行了MethodBeforeAdvice的before方法

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

	private MethodBeforeAdvice advice;


	/**
	 * Create a new MethodBeforeAdviceInterceptor for the given advice.
	 * @param advice the MethodBeforeAdvice to wrap
	 */
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
		return mi.proceed();
	}

}

那么这个适配过程是什么时候发生的呢,当然就是在获取调用链chain时发生的

在这里插入图片描述

跟踪getInterceptorsAndDynamicInterceptionAdvice

在这里插入图片描述

再跟踪

在这里插入图片描述

而registry其实就是DefaultAdvisorAdapterRegistry,初始化时会初始化适配器

在这里插入图片描述

至此,适配器模式结束



版权声明:本文为m0_37637511原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。