
本篇不僅僅是介紹Spring回圈依賴的原理,而且給出Spring不能支持的回圈依賴場景與案例,對其進行詳細決議,同時給出解決建議與方案,以后出現此問題可以少走彎路,
背景
1、回圈依賴例外資訊
-
應用時間時間久 -
應用多人同時并行開發 -
應用保證迭代進度
經常出現啟動時出現回圈依賴例外
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskPunchEvent': Injection of resource dependencies failed; nested exception is org.
springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injected into other be
ans [toVoConvertor] 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. Thi
s is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:325)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1404)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:595)
... 40 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injecte
d into other beans [toVoConvertor] 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 o
f the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:622)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:452)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:637)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:322)
... 51 more
2、依賴關系


3、涉及基礎知識
-
Spring bean 創建流程 -
Dynamic Proxy 動態代理 -
Spring-AOP 原理
問題
1、什么是回圈依賴?
2、為什么會產生回圈依賴?
3、回圈依賴有哪些場景?
4、Spring如何解決回圈依賴的?
5、Spring為什么使用三級快取?
6、Spring支持AOP回圈依賴,為何還存在回圈依賴例外?
7、Spring不支持的回圈依賴場景及如何解決?
注:Spring啟動流程與Bean創建初始化流程如不熟悉,自行補習,篇幅原因此處不做介紹
Spring回圈依賴
1、什么是回圈依賴

2、核心概念
-
BeanDefinition:spring核心bean的配置資訊
-
Spring Bean:spring管理的已經初始化好以后的可使用的實體
-
首先,通過spring通過掃描各種注解 @Compoent、@Service、@Configuration等等把需要交給spring管理的bean初始化成 BeanDefinition 的串列
-
然后,根據 BeanDefinition 創建spring bean的實體
-
Java Bean:Java簡單通過建構式創建的物件
-
Spring通過推斷構造方法后,通過反射呼叫建構式創建的物件
1、什么情況下出現回圈依賴

Spring創建Bean - #DefaultListableBeanFactory#preInstantiateSingletons
@Override
public void preInstantiateSingletons() throws BeansException {
//......
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
//FactoryBean介面處理
......
}
else {
//正常Bean的加載入口
getBean(beanName);
}
}
}
//......
}
-
構造器內的回圈依賴 -
注入的好處很明顯,如果容器中不存在或者存在多個實作時,可以從容處理, -
強依賴,先有雞還是先有蛋問題暫無解,此依賴方式Spring不支持,除非自身實作代理加延遲注入,這種方式很難解決,除非實作類似于lazy生成代理方式進行解耦來實作注入,Spring沒有支持可能因為此種注入場景都可以用其他方式代替且場景極少, -
弱依賴,spring 4.3之后增加 ObjectProvider 來處理
//構造器回圈依賴示例
public class StudentA {
private StudentB studentB ;
public StudentA(StudentB studentB) {
this.studentB = studentB;
}
}
public class StudentB {
private StudentA studentA ;
public StudentB(StudentA studentA) {
this.studentA = studentA;
}
}
-
setter方式單例,默認方式 -
setter方式原型,prototype
對于“prototype”作用域Bean,Spring容器不進行快取,因此無法提前暴露一個創建中的Bean, -
field屬性回圈依賴
最常用,此場景是通過反射注入,以下為@Autowire 注入代碼,@Resource省略
AutowiredAnnotationBeanPostProcessor#postProcessProperties
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = https://www.cnblogs.com/88223100/p/findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
//屬性注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,"Injection of autowired dependencies failed", ex);
}
return pvs;
}
(1)、一級快取
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
-
最基礎的單例快取 -
限制 bean 在 beanFactory 中只存一份,即實作 singleton scope
(2)、二級快取
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
-
看名字應該就能猜到,快取earlySingletonBean,與三級快取配合使用的 -
需要注意:
-
在沒有AOP場景時是可以的,每次earlySingletonObjects.get()換成去三級快取取就可以,存在問題
-
存在AOP場景時
-
因此,讓使用者去做重復性判斷是不可控的,很容易出現問題,于是引入了第二級快取,當呼叫三級快取里的物件工廠的getObject方法之后,getEarlyBeanReference 就會把回傳值放入二級快取,洗掉三級快取,后續其他依賴該物件的Bean獲取的都是同一個earlyBean,保證singleton原則,
-
每次都呼叫 getEarlyBeanReference,即使回傳物件都一致,也浪費不必要時間
-
如果使用者在 getEarlyBeanReference 時直接 new XXX(),則物件又不一致,無法保證 singleton,所以需要使用者熟悉這塊原理,并且自身維護,并且暴露內部實作細節
-
每次都呼叫 getEarlyBeanReference 回傳代理物件都不一致,無法保證 singleton
-
如果沒有此快取,可不可以解決回圈依賴問題?
(3)、三級快取
三級快取(Bean創建時提供代理機會的Bean工廠快取)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
-
所以二級快取和三級快取是組合,不要拆成兩個獨立的東西去理解 -
基于這種設計,沒有發生回圈依賴的bean就是正常的創建流程 -
相互參考的bean 會觸發鏈路中最初結點放入三級快取內容,呼叫 getEarlyBeanReference 回傳相應物件
6、Spring為何不使用一級、二級快取解決回圈依賴
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//創建Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
.....
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");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//填充Bean依賴與Bean的初始化
Object exposedObject = bean;
try {
//填充依賴的bean實體
populateBean(beanName, mbd, instanceWrapper);
//初始化---注意!注意!注意!此方法中可能呼叫 BeanPostProcessor
//的applyBeanPostProcessorsAfterInitialization時可能會回傳代理物件,如果代理途徑與創建時代理方式不同則也會產生不同代理物件
//從而產生回圈依賴中物件不一致情況
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//如果存在回圈依賴,則保證最開始創建的Bean需要是回圈依賴 getEarlyBeanReference觸發生成的bean
//因為getEarlyBeanReference 可能回傳的是代理類,因為singleton必須全域唯一
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
//只有真正存在回圈依賴時,才會觸發 getEarlyBeanReference呼叫產生EarlyBean
//未存在回圈依賴,則getEarlyBeanReference不觸發,earlySingletonReference為null,回傳exposedObject即可
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
......
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.");
}
}
}
}
return exposedObject;
}
三級快取獲取Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//一級快取(單例池)獲取Bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//二級快取獲取(提前暴露不完全)Bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//三級快取Bean的創建工廠獲取bean(可提前被代理)
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
SmartInstantiationAwareBeanPostProcessor重點 -> APC之父
//提供提前創建并回傳代理的工廠singletonFactory.getObject()執行的是個回呼
//addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
//getEarlyBeanReference是SmartInstantiationAwareBeanPostProcessor介面定義方法,
//此方法很關鍵(建構式推斷也在此定義)
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
(1)、相互依賴的Bean只有需要AOP或者動態代理時才有可能出現回圈依賴例外
-
正常情況原始Spring Bean無論怎樣相互依賴都沒有問題,Spring完全可以處理這種場景
-
絕大多數存在AOP場景也都是支持的,Spring支持的
-
只有相互依賴場景下某些Bean需要被動態代理時偶爾會出現回圈依賴例外問題,以下解釋例外場景:
通俗解釋(省略很多細節):A -> B -> C -> A
-
Spring 啟動開始創建 A,doCreateBean()中對A進行屬性填充populateBean()時需要發現依賴B物件,此時A還沒有進行初始化,把A原始物件包裝成SingletonFactory 放入三級快取,
-
A依賴B,因此doCreateBean()會創建B,并對B進行屬性填空populateBean()時需要發現依賴C物件,
-
C依賴A,因此doCreateBean()會創建C,并對C進行屬性填空populateBean()時需要發現依賴A物件,
3.1. 此時去一級快取獲取A,因為A前邊并沒有填充與初始化完成,因此在一級快取中不存在;
3.2. 去二級快取取A,因為A前邊并沒有填充與初始化完成,因此在二級快取中不存在;
3.3. 去三級快取取A,第一步中把A封裝成SingletonFactory放入三級快取的,因此三級快取中可以獲取到A的物件
3.3.1. 此時獲取的A如果有必要會對A進行動態代理,回傳代理物件;
3.3.2. 否則不需要代理則回傳未填充、未初始化的原始物件A;3.4. 獲取到A物件,注入到C中,接著初始化C,回傳C物件;
-
C物件回傳,注入到B中,接著初始化B,回傳B物件;
-
B物件回傳,注入到A中,接著初始化A,問題就在這兒:
5.1. 如接下來初始化A無需被代理
5.1.1. exposedObject回傳是A原始物件,此時與C中被注入A都是原始Bean,完美;5.2. 如接下來初始化A需要被代理:
5.2.1. APC根據快取檢查之前創建A時是否被代理過,如已被代理,直接回傳原始物件,與A原始一致,完美;
5.2.2. 但是,如此時A初始化程序中有獨特的其他BeanPostProcessor,對A的代理方式有單獨處理,則被代理后的proxy2與原始Bean、被注入到C中的A的Proxy均不再一致,拋出例外; -
總結重點:
6.1. 最終原因就是提前暴露的已經注入到C中的A(無論是否被代理)與后來經過初始化后被代理的A(proxy2)不再是同一個Bean;
6.2. 因為Spring管理Bean默認是Singleton的,現在出現了兩個bean,默認情況下無法決斷,因此就拋出了例外,

(2)、各別注解使用不當
-
@Respository
-
處理器 PersistenceExceptionTranslationPostProcessor#postProcessAfterInitialization
-
被 @Respository注解的類在Spring啟動初始化時存在回圈依賴鏈路中,如果此時Spring中開啟了AOP,則必拋出回圈依賴例外
-
所以DAO層使用時,最好不要引入外部業務邏輯,業務邏輯可以提取到Manager、Service層等中,保持DAO純凈
-
案例分析:見第四節
-
@Asyn
-
處理器 AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization
-
被 @Asyn注解的類在Spring啟動初始化時存在回圈依賴鏈路中,如果此時Spring中開啟了AOP,則必拋出回圈依賴例外
-
以上等注解的類使用不當都比較容易出現回圈依賴,這兩個注解同一個父類,造成回圈依賴原理一樣
AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization
(3)、存在多個AutoProxyCreator(APC),出現多層代理
-
InfrastructureAdvisorAutoProxyCreator -
AspectJAwareAdvisorAutoProxyCreator -
AnnotationAwareAspectJAutoProxyCreator
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
//因為三個APC存在能力父子關系,按照指定注冊的APC自動調整優先級,從而保證只存在一個APC
//如未指定APC,則默認為InfrastructureAdvisorAutoProxyCreator
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
從而觸發多個APC的 getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//此時如存在多個APC,則依次執行 getEarlyBeanReference 回傳多層代理物件
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}

最終proxy2會被注入到依賴的Bean中,即例如:A-proxy2 注入到 B中
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
//注意這個Bean可是原始物件,每個APC都快取自身代理過的類,但是存在多個APC時,后續的APC快取的確是代理類的代理
//即如第二個APC是BeanNameAutoProxyCreator,其快取的可是 proxy1的class,原始類在此APC是沒被代理過的,
//因此此時會對原始類進行二次代理,產生Proxy3
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
//視線回傳本次回圈依賴最初實體化的結點:A->B->C->A,則此處為A的創建流程
//此時A 通過 getEarlyBeanReference生成A ->proxy2注入到C中,
//C直接實體創建不會觸發getEarlyBeanReference,注入到B中
//B直接實體創建不會觸發getEarlyBeanReference,注入到A中
//A依賴處理完畢,繼續初始化 initializeBean流程 -> postProcessAfterInitialization,回傳 proxy3
if (earlySingletonExposure) {
//此時獲取到的代理類是 proxy2,即已經注入到依賴類C中的代理,因此不為null
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//多APC時,exposedObject 在之前initializeBean -> postProcessAfterInitialization作用下回傳proxy3
//proxy3 != bean 不一致,違反了singletion原則,因此會拋出回圈依賴例外
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
......
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
8、正常AOP代理為何沒問
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
//提前通過singletonFactory.getObject()創建的代理快取起來以后,這里如果再次判斷需要代理,
//快取中存在已被代理則直接回傳原始bean,無需再次代理,后續直接獲取earlySingletonReference,
//因此前后代理出來的物件是一致的
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
解決方案
1、無需代理場景使用原始物件
-
原始物件相互注入沒有問題,檢查不許要生成代理的類

2、@lazy解耦
-
原理是發現有@lazy注解的依賴為其生成代理類,依賴代理類,從而實作了解耦
-
@Lazy 用來標識類是否需要延遲加載;
-
@Lazy 可以作用在類上、方法上、構造器上、方法引數上、成員變數中;
-
@Lazy 作用于類上時,通常與 @Component 及其衍生注解配合使用;
-
@Lazy 注解作用于方法上時,通常與 @Bean 注解配合使用;

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
......
else {
//處理@lazy
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
ContextAnnotationAutowireCandidateResolver#isLazy
//是否為@lazy,如果為@lazy則創建依賴代理
protected boolean isLazy(DependencyDescriptor descriptor) {
for (Annotation ann : descriptor.getAnnotations()) {
Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
.......
}
3、抽取公共邏輯
-
業務層面重構,不再相互依賴而是依賴公共模塊,并且各個對外業務與內部介面拆分
案例(可直接運行)
1、@Repository案例分析
import org.junit.Test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
* @author: Superizer
*/
@Component
public class MainSpringCircularDependencyTester
{
@Test
public void springCircularDependencyTest()
{
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringCircularDependencyConfig.class);
X x = ac.getBean(X.class);
System.out.println("Spring bean X =" + x.getClass().getName());
x.display();
Y y = ac.getBean(Y.class);
System.out.println("Spring bean Y =" + y.getClass().getName());
y.display();
Z z = ac.getBean(Z.class);
System.out.println("Spring bean Z =" + z.getClass().getName());
z.display();
System.out.println("******************Main********************");
}
@Configuration
@ComponentScan("com.myself.demo.spring.v5.circular.dependency")
// @EnableAspectJAutoProxy
@ConditionalOnClass(PersistenceExceptionTranslationPostProcessor.class)
static class SpringCircularDependencyConfig{
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.dao.exceptiontranslation", name = "enabled",
matchIfMissing = true)
public static PersistenceExceptionTranslationPostProcessor
persistenceExceptionTranslationPostProcessor(Environment environment) {
PersistenceExceptionTranslationPostProcessor postProcessor = new PersistenceExceptionTranslationPostProcessor();
boolean proxyTargetClass = environment.getProperty(
"spring.aop.proxy-target-class", Boolean.class, Boolean.TRUE);
postProcessor.setProxyTargetClass(proxyTargetClass);
return postProcessor;
}
}
abstract static class A {
public abstract A injectSources();
public abstract A self();
public void display(){
System.out.println("injectSources:" + injectSources().getClass().getName());
System.out.println("*******************************************************");
}
}
//X、Y、Z 只要回圈依賴中第一個類X有注解@Repository,就會出現回圈依賴例外
//執行X的singletonFactory.getObject()回傳的原物件,但是后邊初始化時
//執行到PersistenceExceptionTranslationPostProcessor時單獨創建代理邏輯回傳的是代理類
//exposedObject = initializeBean(beanName, exposedObject, mbd);
@Repository
// @Component
static class X extends A{
@Resource
private Y y;
@Override
public Y injectSources()
{
return y;
}
@Override
public X self() {
return this;
}
}
@Component
// @Repository
static class Y extends A{
@Resource
private Z z;
@Override
public Z injectSources() {
return z;
}
@Override
public Y self()
{
return this;
}
}
@Component
// @Repository
static class Z extends A{
@Resource
private X x;
@Override
public X injectSources()
{
return x;
}
@Override
public Z self()
{
return this;
}
}
}
2、多AutoProxyCreator場景
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.aop.support.AbstractExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
/**
* @author: Superizer
* Copyright (C) 2021
* All rights reserved
*/
@Component
public class MainSpringCircularDependencyV2Tester
{
@Test
public void circularDependencyV2Tester()
{
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringCircularDependencyConfig.class);
A a = ac.getBean(A.class);
System.out.println("Spring bean A =" + a.getClass().getName());
a.display();
B y = ac.getBean(B.class);
System.out.println("Spring bean B =" + y.getClass().getName());
y.display();
C z = ac.getBean(C.class);
System.out.println("Spring bean C =" + z.getClass().getName());
z.display();
System.out.println("******************Main********************");
}
@Configuration
@ComponentScan("com.myself.demo.spring.v5.circular.dependency.v2")
@EnableAspectJAutoProxy
static class SpringCircularDependencyConfig {
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
Pointcut pointcut = new AbstractExpressionPointcut() {
@Override
public ClassFilter getClassFilter() {
return (tmp) -> {
String name = tmp.getName();
if(name.equals(A.class.getName())) {
return true;
}
return false;
};
}
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
};
advisor.setPointcut(pointcut);
advisor.setAdvice(new SpringAopAroundMethod());
advisor.setOrder(0);
return advisor;
}
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator apc = new BeanNameAutoProxyCreator();
apc.setBeanNames("a");
apc.setOrder(-1);
apc.setProxyTargetClass(true);
return apc;
}
}
abstract static class G {
public abstract G injectSources();
public abstract G self();
public void display(){
System.out.println("injectSources:" + injectSources().getClass().getName());
System.out.println("*******************************************************");
}
}
@Component(vhttps://www.cnblogs.com/88223100/p/alue = "a")
static class A extends G {
@Resource
private B b;
@Override
public B injectSources()
{
return b;
}
@Override
public A self() {
return this;
}
}
@Component
static class B extends G {
@Resource
private C c;
@Override
public C injectSources() {
return c;
}
@Override
public B self()
{
return this;
}
}
@Component
static class C extends G {
@Resource
private A a;
@Override
public A injectSources()
{
return a;
}
@Override
public C self()
{
return this;
}
}
static class SpringAopAroundMethod implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Aop Before method!");
try {
Object result = methodInvocation.proceed();
System.out.println("Aop after method!");
return result;
} catch (IllegalArgumentException e) {
System.out.println("Aop throw exception!");
throw e;
}
}
}
}
總結
Spring流程圖


本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/What-are-the-things-that-Spring-loops-rely-on.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552768.html
標籤:Java
上一篇:Java中的字串
下一篇:返回列表
