spring.aop 随笔3 依赖循环场景下的自调用

0. 最近的外卖多少会撒,好 好 好

Spring事务处理时自我调用的解决方案及一些实现方式的风险


  • 依赖循环的场景下会出现自调用问题(aop代理没有注入,实际注入真实的bean实例,比如事务)的时候,往往是因为循环依赖的副作用,本文旨在分析"咋搞的"

  • 通常说的自调用,一般没有依赖循环的前置条件,通过BeanPostProcessor注入代理实例可以轻松拿下(本文所描述的场景并不能直接拿下)

1. 先做个引导:为什么需要自调用

AOP拦截器除了执行增强的前置、后置、环绕通知方法以外,在调用我们预设的、需要被增强的方法时,并不是调用代理对象的,而是我们自己写的、真实的、交给springbean池管理的实例(通过Class反射调用)。

因此,我们在处理AOP方法在自身类里面嵌套的时候,就自然而然地会想到去自动获取当前类的代理实例来解决这个问题。

2. 没有循环依赖的时候,自调用是怎么正常运转的

我们从IOC过程中接入这个话题

	// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		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的代理类工厂(三级缓存使用)
			// 方法入参中的bean为未完成初始化的bean真实实例
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// 属性注入
			populateBean(beanName, mbd, instanceWrapper);
			// step into ...
			// 属性注入后执行bean的初始化方法
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}
	
	// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
	protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// step into ...
			// 我们自定义用于自调用注入的beanPostProcessor在这里执行
			// 题外话:aop代理类(beanPostProcessor)也是在这里生成代理实例
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}
	
	// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		// 初始值设为入参中的bean真实实例
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			// step into ...
			// 可以想到,这里的result是前一次循环的值
			// 通过debug可以观察到BeanPostProcessors的执行顺序:
			// 		...
			// 		AnnotationAwareAspectAutoProxyCreator(AOP切面用于生成代理实例的beanPostProcessor,并返回代理实例)
			// 		InjectBeanSelfProcessor(我们自定义,用于注入代理对象的beanPostProcessor)
			// 		CommonAnnotationBeanPostProcessor
			// 		AutowiredAnnotationBeanPostProcessor
			// 		...
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

	// AbstractAutoProxyCreator 与 AnnotationAwareAspectAutoProxyCreator 的关系见下图
	// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// step into ...
			// 如果earlyProxyReferences缓存不存在A的实例时,返回其代理实例
			// 问题就出在这里,A在执行这段代码时,发现缓存中是有他自己的真实实例的,故直接返回了
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// 返回AOP代理实例,不是重点
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		// 于是就通过这里,A就直接给自己注入bean(入参的bean指A的真实对象)
		return bean;
	}

在这里插入图片描述

附上我们自定义用于自调用注入代理实例的beanPostProcessor

	@Component
	public class InjectBeanSelfProcessor
	        implements BeanPostProcessor
	{
	    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	        return bean;
	    }
	    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	    	//如果Bean实现了BeanSelfAware标识接口,就将代理对象注入
	        if(bean instanceof BeanSelfAware) {
	        	//即使是prototype Bean也可以使用此种方式
	            ((BeanSelfAware) bean).setSelf(bean); 
	        }
	        return bean;
	    }
	}

3. 依赖循环下该注入方式是如何裂开的

前面的随笔对依赖循环的过程已经说的过于详细了,这里放一张简图直接略过:

请添加图片描述

现在我们回过看一下前面伏笔:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
该方法原意为B实例提供了返回A代理的出口,但有一个"副作用",让我们走进getEarlyBeanReference(beanName, mbd, bean)

	// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		// 回顾可知,这里的初始值为bean的真实实例
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			// 跟前面的一样,这里将访问AOP切面的BeanPostProcessor用于为B返回A实例
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					// step into ...
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

	// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		// 在这里缓存了A的真实实例
		this.earlyProxyReferences.put(cacheKey, bean);
		// 这里返回A的代理实例
		return wrapIfNecessary(bean, beanName, cacheKey);
	}
引用\[1\]中提到的错误是关于Spring AOP的切点配置路径有误导致的。解决方法是将原配置中的`execution(* com.kang.service.(…))`修改为`execution( com.kang.service.ServiceImpl.(…))`。这样可以正确匹配到切点。\[1\] 引用\[2\]中提到的解决方法是导入了`org.aspectj.aspectjweaver`的依赖,版本为`1.8.14`。这个依赖可能是解决问题的关键。\[2\] 引用\[3\]中提到的错误可能是由于Spring AOP代理混用导致的。解决方法是在XML配置文件中添加`<aop:aspectj-autoproxy proxy-target-class="true"/>`来解决这个问题。\[3\] 综上所述,如果在`com.spring.aop.aspectj`下不成功,可能是由于切点配置路径有误、缺少依赖或者代理混用等原因导致的。你可以根据上述提到的解决方法来尝试解决这个问题。 #### 引用[.reference_title] - *1* [Error creating bean with name ‘org.springframework.aop.aspectj.AspectJPointcutAdvisor#0](https://blog.csdn.net/quest101/article/details/117877362)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [spring AOP 错误 org.springframework.aop.aspectj.AspectJPointcutAdvisor 等等](https://blog.csdn.net/weixin_30278237/article/details/97573038)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Failed to instantiate [org.springframework.aop.aspectj.AspectJExpressionPointcut]: No default constr](https://blog.csdn.net/thetimelyrain/article/details/96509488)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肯尼思布赖恩埃德蒙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值