Spring源码——AOP实现原理

引言

Spring AOP(Aspect Orient Programming),AOP翻译过来就是面向切面编程,它体现的是一种编程思想,是对面向对象编程(OOP)的一种补充。
在实际业务开发过程中,有一些代码,跟业务没有任何关系,但在很多地方又会用到,比如:记录日志、计算执行时间、事务、权限验证等等,这些代码跟我们实际业务没有一丁点关系,但在许多的地方又会用到,我们传统的方式,肯定是通过复制粘贴把它粘进需要用到的地方,但这样做,会对原本的业务代码块造成很高侵入性,使代码的阅读性、可维护性变得很糟糕,所以就延伸出来了AOP的概念,它不是针对于某个编程语言、某段代码、而是体现了一种编程思想,通过切面的方式将非业务代码切入到核心业务处理流程中,减少对业务代码的修改,降低对业务代码的侵入程度。

Spring作为一个Bean对象的管理框架,提供了AOP功能集成的扩展接口(比如:事务)。

AOP动态代理

AOP是借助了动态代理的实现机制,通过为需要被切入的对象生成动态代理的方式来实现的。动态代理相关原理请参考笔者另一篇博客———设计模式——动态代理

AOP相关术语

AOP用到的一些专业术语

1. 通知(Advice)

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

2. 连接点(join point)

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

3. 切点(PointCut)

可以插入增强处理的连接点。

4. 切面(Aspect)

切面是通知和切点的结合。

5. 引入(Introduction)

引入允许我们向现有的类添加新的方法或者属性。

6. 织入(Weaving)

将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

Spring AOP

在Spring对Bean对象的创建过程中,会将已实例化但尚未初始化的 Bean对象(半成品),最先放入到三级缓存(singletonFactories)中,而在放入的时候,并不是放入的一个实例对象那么简单,它是将获取这个半成品的工厂方法给放入了三级缓存中,然后再对这个半成品进行属性填充、初始化等操作,这里也是解决循环依赖的核心所在,在后续代码执行中,就可以通过beanName在三级缓存中,获取到这个ObjectFactory对象,通过调用ObjectFactory的getObject方法,获取之前创建的半成品。
而对于AOP对象的动态代理对象的创建,也就是在这个从三级缓存(半成品) 放入 二级缓存(成品的动态代理)过程中的进行处理的。

以下doCreateBean方法里省略了部分代码

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException{
...
		// 判断当前bean是否需要提前曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			// 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
...

allowCircularReferences这个值默认等于true。以上代码调用了addSingletonFactory方法,实际上它是向三级缓存中添加了一个lambd表达式

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		// 使用singletonObjects进行加锁,保证线程安全
		synchronized (this.singletonObjects) {
			// 如果单例对象的高速缓存【beam名称-bean实例】没有beanName的对象
			if (!this.singletonObjects.containsKey(beanName)) {
				// 将beanName,singletonFactory放到单例工厂的缓存【bean名称 - ObjectFactory】
				this.singletonFactories.put(beanName, singletonFactory);
				// 从早期单例对象的高速缓存【bean名称-bean实例】 移除beanName的相关缓存对象
				this.earlySingletonObjects.remove(beanName);
				// 将beanName添加已注册的单例集中
				this.registeredSingletons.add(beanName);
			}
		}
	}

lambd表达式里面的方法,getEarlyBeanReference(beanName, mbd, bean)方法源码

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		// 默认最终公开的对象是bean,通过createBeanInstance创建出来的普通对象
		Object exposedObject = bean;
		// mbd的systhetic属性:设置此bean定义是否是"synthetic",一般是指只有AOP相关的pointCut配置或者Advice配置才会将 synthetic设置为true
		// 如果mdb不是synthetic且此工厂拥有InstantiationAwareBeanPostProcessor
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			// 遍历工厂内的所有后处理器
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				// 如果bp是SmartInstantiationAwareBeanPostProcessor实例
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					// 让exposedObject经过每个SmartInstantiationAwareBeanPostProcessor的包装
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		// 返回最终经过层次包装后的对象
		return exposedObject;
	}

三级缓存中添加的是一个获取对象的lambd表达式,所以这个表达式只是添加进去了,并不会立刻运行,以上就是lambd表达式里面的内容
在lambd表达式里面,我们看到SmartInstantiationAwareBeanPostProcessor这样的一个接口,它属性BPP。
对于每个Bean对象都会遍历BPP,如果是BPP是SmartInstantiationAwareBeanPostProcessor的实现类,就会运行它的getEarlyBeanReference(exposedObject, beanName)方法。
而对于开启了AOP注解(@EnableAspectJAutoProxy)功能的项目,会添加一个AnnotationAwareAspectJAutoProxyCreator,它就属性SmartInstantiationAwareBeanPostProcessor(BPP)

看到这里,大家就应该明白了,AOP功能也是通过对BPP的扩展来实现的。
AnnotationAwareAspectJAutoProxyCreator继承了父类AbstractAutoProxyCreator中的getEarlyBeanReference方法

	/**
	 * 放到集合中,然后判断要不要包装,其实就是在循环依赖注入属性的时候如果有AOP代理的话,也会进行代理,然后返回
	 * @param bean the raw bean instance
	 * @param beanName the name of the bean
	 * @return
	 */
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

核心代码wrapIfNecessary(bean, beanName, cacheKey),到此,我们大致明白了,AOP的基本处理流程了(其实因为AnnotationAwareAspectJAutoProxyCreator属于BPP,在执行BPP的后置处理器有时候也有机会执行wrapIfNecessary方法,并返回代理对象)

	/**
	 * 先判断是否已经处理过,是否需要跳过,跳过的话直接就放进advisedBeans里,表示不进行代理,如果这个bean处理过了,获取通知拦截器,然后开始进行代理
	 *
	 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
	 * @param bean the raw bean instance
	 * @param beanName the name of the bean
	 * @param cacheKey the cache key for metadata access
	 * @return a proxy wrapping the bean, or the raw bean instance as-is
	 */
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// 如果已经处理过,直接返回
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// 这里advisedBeans缓存了已经进行了代理的bean,如果缓存中存在,则可以直接返回
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 这里isInfrastructureClass()用于判断当前bean是否为Spring系统自带的bean,自带的bean是
		// 不用进行代理的;shouldSkip()则用于判断当前bean是否应该被略过
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			// 对当前bean进行缓存
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}
		// Create proxy if we have advice.
		// 获取当前bean的Advices和Advisors
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// 对当前bean的代理状态进行缓存
		if (specificInterceptors != DO_NOT_PROXY) {
			// 对当前bean的代理状态进行缓存
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 根据获取到的Advices和Advisors为当前bean生成代理对象
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// 缓存生成的代理bean的类型,并且返回生成的代理bean
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

Spring AOP生成代理类时JDK代理和CGLIB代理的选择

/**
	 * 真正的创建代理,判断一些列条件,有自定义的接口的就会创建jdk代理,否则就是cglib
	 * @param config the AOP configuration in the form of an
	 * AdvisedSupport object
	 * @return
	 * @throws AopConfigException
	 */
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// 这段代码用来判断选择哪种创建代理对象的方式
		// config.isOptimize()   是否对代理类的生成使用策略优化 其作用是和isProxyTargetClass是一样的 默认为false
		// config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象 默认为false
		// hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是SpringProxy类型
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			// 上面的三个方法有一个为true的话,则进入到这里
			// 从AdvisedSupport中获取目标类 类对象
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			// 判断目标类是否是接口 如果目标类是接口的话,则还是使用JDK的方式生成代理对象
			// 如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 配置了使用Cglib进行动态代理或者目标类没有接口,那么使用Cglib的方式创建代理对象
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			// 使用JDK的提供的代理方式生成代理对象
			return new JdkDynamicAopProxy(config);
		}
	}

Spring AOP生成动态代理大致流程如下

两种代理方式执行逻辑

AOP执行逻辑

AOP代理方式有两种:JDK代理 和 CGLIB代理

JdkDynamicAopProxy

JdkDynamicAopProxy类是实现了反射中的InvocationHandler接口

所以调用过程中的核心执行方法在invoke方法中
invoke方法中核心代码块

				// We need to create a method invocation...
				// 将拦截器封装在ReflectiveMethodInvocation,以便于使用其proceed进行处理
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				// 执行拦截器链
				retVal = invocation.proceed();

看得出来,它的执行是交给一个MethodInvocation去执行的,而ReflectiveMethodInvocation是一个执行器链,里面根据我们的配置有可能包含了以下五种通知

采用责任链模式对自定义配置的五种通知进行依次调用

ObjenesisCglibAopProxy

posted @ 2021-05-05 17:14  心若向阳花自开  阅读(700)  评论(0编辑  收藏  举报