源码分析-Spring AOP是如何实现的?(一)

本文目的

此篇博客主要目的是要理清Spring中实现AOP这一编程思想的的具体思路。由于Spring AOP的源码非常多且繁琐,所以这里着重介绍Spring对AOP的核心源码实现,一些辅助功能的源码实现细节不会过多讲述,同时,我们知道,Spring 对目标类的代理有两种方式----JDK Proxy和Cglib,本篇主要以JDK这种代理方式来分析。既然说Spring AOP的源码,需要读者至少要知道AOP如何使用,同时也要对Spring IOC的实现流程或者说bean的生命周期有部分了解,这部分知识本文不会过多讲诉。

在写这篇博客前,博主也在CSDN上看了很多篇Spring AOP相关的文章,每篇博客的侧重点不一样。有些着重解释了AOP的相关专有名词介绍,有些大量篇幅介绍了动态代理而忽略了Spring对其的实现思路,有些博客忽略了Advisor的获取和解析讲解,最重要一点不知道什么原因,这些热门博客都是以XML配置这种方式来作为AOP的入口,倒不是说XML有什么不好,XML和注解它们在底层实现基本是一致的。不过现在普遍流行Spring Boot或者说是注解驱动开发,所以本文会以纯Java,且最简化的AOP配置来进行源码分析。在本篇博客的结尾,我也会向大家推荐几篇个人认为不错的文章。

初始化AOP环境

首先我们先搭建一个AOP的环境,这里我不会用其它博客所使用的AplicationContext这种方式来初始化Spring环境,熟悉IOC源码的同学会知道,ApplicationContext扩展了BeanFactory,它有了很多复杂的功能,例如事件发布,Environment环境,国际化等等。这些复杂的功能,我们这里不需要,我们只要纯粹的研究AOP的功能,我们不要让其它的不相关功能(各种BeanPostProcessor的实现)影响我们去影响或混淆了我们思路,所以建议使用下面的初始化代码来学习AOP。

最简方式AOP环境配置

测试入口类

/**
 * @Description 测试类
 * @Author binghua.zheng
 * @Date 2021/1/15 22:39
 * @Version 1.0.0
 */
public class Test {

	public static void main(String[] args) {
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		// 注册AOP功能所需要的后置处理器--AnnotationAwareAspectJAutoProxyCreator
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(beanFactory);
		// 提前实例化后置处理器
		BeanPostProcessor beanPostProcessor = beanFactory.getBean(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME, BeanPostProcessor.class);
		// 将后置处理器添加到beanFactory中
		beanFactory.addBeanPostProcessor(beanPostProcessor);
		// 注册目标类bean和切面bean
		registryBeanDefinition(beanFactory);
		// 获取UserService接口的代理类
		UserService userService = beanFactory.getBean(UserService.class);
		userService.getUserInfo(100L);

	}

	private static void registryBeanDefinition(BeanDefinitionRegistry registry) {
		// 注册目标类BeanDefinition--userService
		RootBeanDefinition userServiceBeanDefinition = new RootBeanDefinition();
		userServiceBeanDefinition.setBeanClass(UserServiceImpl.class);
		registry.registerBeanDefinition("userService", userServiceBeanDefinition);
		
		// 注册切面配置类BeanDefinition--userAspect
		RootBeanDefinition aspectBeanDefinition = new RootBeanDefinition();
		aspectBeanDefinition.setBeanClass(UserAspect.class);
		registry.registerBeanDefinition("userAspect", aspectBeanDefinition);
	}
}

目标类接口–UserService

public interface UserService {

	void getUserInfo(Long userId);
}

目标类–UserService

/**
 * @Description 目标类
 * @Author binghua.zheng
 * @Date 2021/1/15 22:25
 * @Version 1.0.0
 */
public class UserServiceImpl implements UserService {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

	@Override
	public void getUserInfo(Long userId) {
		log.info("select user info for userId -> {}", userId);
	}
}

切面配置类

/**
 * @Description 切面
 * @Author binghua.zheng
 * @Date 2021/1/15 22:30
 * @Version 1.0.0
 */
@Aspect
public class UserAspect {

	private static final Logger log = LoggerFactory.getLogger(UserAspect.class);

	@Pointcut("execution(* com.binghuazheng.aop.bean.UserService.getUserInfo(*))")
	private void pointCut(){}

	@Before("pointCut()")
	public void adviceBefore(JoinPoint joinPoint) {
		log.info("advice ---->>>> Before");
	}

	@After("pointCut()")
	public void adviceAfter() {
		log.info("advice ---->>>> After");
	}

	@AfterReturning("pointCut()")
	public void adviceAfterReturning() {
		log.info("advice ---->>>> AfterReturning");
	}

	@Around("pointCut()")
	public Object adviceAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		log.info("advice ---->>>> Around Start");
		Object result = proceedingJoinPoint.proceed();
		log.info("advice ---->>>> Around End");
		return result;
	}

	@AfterThrowing(value = "pointCut()", throwing = "ex")
	public void adviceAfterThrowing(Throwable ex) {
		log.info("advice ---->>>> AfterThrowing");
	}

}

好,以上就是实现AOP功能所需要的最小化配置。因为不需要依赖注入,所以没有注册其它的后置处理器。试一试能否成功。

2021-01-15 22:51:36 [main] INFO  c.b.a.c.UserAspect advice ---->>>> Around Start
2021-01-15 22:51:36 [main] INFO  c.b.a.c.UserAspect advice ---->>>> Before
2021-01-15 22:51:36 [main] INFO  c.b.a.b.i.UserServiceImpl select user info for userId -> 100
2021-01-15 22:51:36 [main] INFO  c.b.a.c.UserAspect advice ---->>>> Around End
2021-01-15 22:51:36 [main] INFO  c.b.a.c.UserAspect advice ---->>>> After
2021-01-15 22:51:36 [main] INFO  c.b.a.c.UserAspect advice ---->>>> AfterReturning

代理逻辑都打印出来了,可以看出,同一Aspect下各种advice的执行顺序是 Around -> Before -> After -> AfterReturning。

注解方式AOP环境配置

在我们平时的项目中,一般使用的注解驱动开发这种方式来开启我们的AOP环境。
所使用的注解就是 @EnableAspectJAutoProxy
我没有使用注解开发,而手动添加AnnotationAwareAspectJAutoProxyCreator这个bean,其实也是参考了此注解的源码。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

可以看到此注解内部通过@Import导入了AspectJAutoProxyRegistrar这个AOP注册器。我们再看一看此注册器到底做了什么事。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 注册AOP功能所需要的后置处理器--AnnotationAwareAspectJAutoProxyCreator
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			// 是否使用Cglib代理,默认Jdk代理
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			// 是否将此代理类,暴露给AopContext。仅当前线程有效。
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

可以看到,此注册器所实现的registerBeanDefinitions方法第一行就是直接通过BeanDefinitionRegistry注册了AnnotationAwareAspectJAutoProxyCreator这个注解版的代理类生成器,后边代码就是判断是否使用Cglib来创造代理类和是否暴露这个代理类(暴露后可以使用AopContext.currentProxy()获取代理类,防止同一个代理类中方法互相调用导致AOP失效)。

Spring实现AOP功能的步骤

首先,先不要直接看源码。我们先思考一下,Spring如果要实现这一功能,它必须要经历哪些步骤?
以我们的示例代码功能来举例。

1. 解析收集切面

首先,我们得先知道在Spring容器中都有哪些 切面(Aspect) 吧。因为我们需要根据切面内具体的 切点(Pointcut) 来确定要代理哪些类;并且我们要获取切面内所有的代理逻辑也就是 通知(Advice),根据每一个通知来搭配切点作为一个 通知器(Advsior),才可以针对某个 目标类(target class)目标方法(target method) 进行方法 增强(enhancer)

以上几个术语要牢记,以上几个术语要牢记,以上几个术语要牢记!!!

这一过程是必须要优先于实例化普通bean阶段前执行,或者它必须要在Spring实例化普通bean后,将bean放入容器内前这两个节点去执行。不然,bean如果都实例化完毕放入容器内了,就没机会去进行解析和动态代理了。

2.实例化bean

其次,新婚夜,你想入洞房,那不得有个媳妇或老公啊!通知器已经有了,既然要对方法进行增强,那必须要先创建一个目标对象了。

3.判断此bean是否需要被代理

现在目标对象已经有了,Advisor也有了,万事俱备了,可以入洞房了么?当然不能了,不得检查一下新娘是否上错了花轿吧。这么多Advisor,不可能全部适用于此目标对象把,所以需要遍历且过滤这些Advisor,通过Advisor内部的Pointcut判断是否匹配此目标对象。只要有一个Advisor匹配,就可以准备对此目标对象进行代理。

4.将目标对象变成代理对象

现在已经确定此目标对象中的某个方法需要被增强,那么就要对此目标对象进行代理了。毕竟代理模式本身就是为了增强方法么!动态代理的方式有两种,JDK和Cglib。一个针对接口,一个针对类。

5.完成代理,放入容器内

代理对象生成了,类型如同$Proxy2156(JDK)或者Target$EnhancerBySpringCGLIB$8b53f595(Cglib)。如此,实例化结束,可以将此代理对象放入Spring容器内了,以便后期依赖注入给其它bean使用。

6.执行代理对象方法

准备执行代理对象方法,但这时候有个关键问题。程序此时并不知道要执行的这个代理对象方法是否需要被增强。同一个类中的A和B方法,当前Advisor仅针对A方法增强,忽略B方法,那么此类也会被创建成代理对象。当我调用其方法时,就需要判断了。遍历所有的 Advisor,再通过其中的Pointcut判断是否匹配此B方法,如果不匹配,就直接执行原目标对象方法。反之,如果匹配此A方法,就收集这个Advisor。然后执行其中的代理逻辑,也就是Advice了。

以上就是AOP代理的基本实现步骤。Spring也的确是按照这几个步骤来实现AOP功能的。不过,需要注意的就是Spring在每一个步骤它的实现节点是哪里。带着这些思考,准备进行源码分析吧。

源码分析-AOP功能的实现

如果要分析AOP,那就离不开IOC的执行流程。毕竟如果我们不去干扰bean的实例化操作,那么所有的bean的创建都要经历IOC的各个阶段及其中的后置处理器,我这里主要侧重AOP功能所涉及的后置处理器会着重讲解。
AOP的核心功能是代理对象的创建,从一个目标类在经历了spring的初始化后,变成了代理类,我们可以猜到,一定是IOC过程中,某个节点对其进行了特殊操作。而IOC的过程,就像是一个插拔式或者说模块化定制的一个流水线,因为Spring为我们提供了很多扩展点,这些扩展点分别会在不同的阶段被回调。Spring的DI功能或下面要说的AOP功能就是其中的一个扩展点,就像下图所示。
在这里插入图片描述
Bean Creator本质就是我们常说的BeanFactory,负责实例化并管理我们的bean对象。我们可以手动向这个BeanFactory内根据需求提前加入后置处理器,比如我们一开始的初始化代码,我仅仅加入了AnnotationAwareAspectJAutoProxyCreator这个后置处理器,用于创建我们的代理对象,我暂时不需要依赖注入等其它操作,所以其它的后置处理器就暂时不添加。如果使用常规的ApplicationContext这种上下文来作为代码入口,其内部会自动添加很多的后置处理器以及预设的Bean对象,不利于调试代码。

AOP操作执行节点分析

这里,我们就主要研究AnnotationAwareAspectJAutoProxyCreator这个类的处理逻辑了及实现了哪些后置处理器。看一看此类的继承关系。

在这里插入图片描述
可以看到,此类实现了SmartInstantiationAwareBeanPostProcessor类,而SmartInstantiationAwareBeanPostProcessor类有继承了InstantiationAwareBeanPostProcessor类,InstantiationAwareBeanPostProcessor类又继承BeanPostProcessor类。我们要先知道这几个后置处理器是在Bean的生命周期中哪个节点起作用?主要作用是什么?

  • SmartInstantiationAwareBeanPostProcessor
    predictBeanType:不固定作用节点,用于获取此bean的类型。
    determineCandidateConstructors:是在实例化bean前,用于指定实例化此bean所用的构造器。
    getEarlyBeanReference:是依赖注入时使用,特别时循环依赖时会执行,正常流程不会执行此方法,仅使用Lambda表达式作为方法行为参数。
  • InstantiationAwareBeanPostProcessor
    postProcessBeforeInstantiation:实例化bean前会执行,可以优先于正常流程提前根据Class返回bean对象。
    postProcessAfterInstantiation:实例化bean后执行,可以对bean执行一些初始化操作,返回值可以判断是否要继续执行Spring的依赖注入操作。
    postProcessPropertyValues:实例化bean后,早于依赖注入前执行,用于添加或修改此bean的依赖注入值,影响后期的依赖注入行为。
  • BeanPostProcessor
    postProcessBeforeInitialization:实例化bean后,且依赖注入后,早于此bean的初始化方法前执行。
    postProcessAfterInitialization:实例化bean后,且此bean的初始化方法执行后才回调。
动态代理操作的执行节点

根据以上后置处理器中方法的执行时机,我们大致可以猜出,如果要代理一个bean,最好的执行节点就是在BeanPostProcessor的postProcessAfterInitialization方法内实现。因为此时的bean的属性已经全部注入,且自身的初始化方法也已执行,动态代理行为就是bean生命周期最后的操作。

收集并解析切面的执行节点

我们之前说过,AOP步骤,第一步是要先收集并解析获取容器内的切面,那这一重要操作又是在哪个节点呢?

此操作的要求是必须在其它普通bean对象创建完放入容器前解析。

其实上面的执行节点很多都可以实现,但有代表性的,且也是Spring所实现的,有两处方法来执行此操作。其一,是BeanPostProcessor中的postProcessAfterInitialization方法内,也就是说在实例化且初始化bean后,在创建代理对象这个操作前,去解析所有的切面,然后缓存起来。其二,就是在实例化对象前,也就是InstantiationAwareBeanPostProcessor内的postProcessBeforeInstantiation方法内去解析然后缓存起来。

简单分析一下,InstantiationAwareBeanPostProcessor中的postProcessBeforeInstantiation方法内执行肯定是没有任何问题的,它是在实例化对象前执行,因为此时容器内还没有任何创建好的bean,不会影响代理操作。而BeanPostProcessor中的postProcessAfterInitialization这个节点,勉强有踩点上火车的感觉,不过它至少也上了火车,个人觉得此节点去收集解析切面的原因,是由于Spring这个框架太庞大,功能太多,可以为我们使用的方法也太多,你想想,Spring Framework又要满足基本的核心功能实现,同时又要给我们使用去扩展。
举个例子,如下面代码。

public class BeanFactoryOperation implements BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 *
	 * @param beanFactory the bean factory used by the application context
	 * @throws BeansException in case of errors
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		UserService userService = new UserServiceImpl();
		UserService userServiceProxy = (UserService)beanFactory.initializeBean(userService, "userService");
		userServiceProxy.getUserInfo(100L);
	}
}

此类实现了BeanFactoryPostProcessor接口,会在正常Spring进行实例化所有BeanDefinition阶段前,扫描解析BeanDefinition末尾阶段执行,可以拿到BeanFactory去做一些操作。先不要管上面是否合理,总之它可以提前调用initializeBean方法,而此方法是Spring中Bean生命周期最后才执行,主要就是做一些初始化Bean的操作,其中就包括BeanPostProcessor中postProcessAfterInitialization方法的回调。Spring既然为BeanFactory设计了这么一个API,那AOP所对应的后置处理器就一定要支持想要的功能。

篇幅有限,暂时介绍到这里。下篇博客专门从源码的角度分析Spring AOP实现。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值