Spring事务处理时自我调用的解决方案及一些实现方式的风险
依赖循环的场景下会出现自调用问题(aop代理没有注入,实际注入真实的bean实例,比如事务)的时候,往往是因为循环依赖的副作用,本文旨在分析"咋搞的"
通常说的自调用,一般没有依赖循环的前置条件,通过BeanPostProcessor注入代理实例可以轻松拿下(本文所描述的场景并不能直接拿下)
AOP拦截器除了执行增强的前置、后置、环绕通知方法以外,在调用我们预设的、需要被增强的方法时,并不是调用代理对象的,而是我们自己写的、真实的、交给springbean池管理的实例(通过Class反射调用)。
因此,我们在处理AOP方法在自身类里面嵌套的时候,就自然而然地会想到去自动获取当前类的代理实例来解决这个问题。
我们从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;
}
}
前面的随笔对依赖循环的过程已经说的过于详细了,这里放一张简图直接略过:
现在我们回过看一下前面伏笔:
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);
}