Cristian Spiescu opened SPR-16181 and commented
I have the following case: A -> B -> C -> D -> B. So we have a circular dependency/loop.
If all my objects have @Service
+ @Transactional
, then everything works fine.
If B has @Repository
, then an error occurs, with the message:
Error creating bean with name 'b': Bean with name 'b' has been injected into other beans [d] 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.
I was curious to see what happens, and why the first case worked well but not the second. Here is the stack trace, at the instance where D has been created and populated with a proxy of B (common to the 2 cases):
!spring-bug-1.png|thumbnail!
B is proxified by AnnotationAwareAspectJAutoProxyCreator.getEarlyBeanReference(), cf. the image above.
And here is how the cases differ. In AbstractAutowireCapableBeanFactory.doCreateBean("b") we have the following code:
!spring-bug-2.png|thumbnail!
if "B" has @Repository
, initializeBean() will return a proxy of B, and not B; hence further down in the code the error will be thrown. If no @Repository
exists => initializeBean() returns the same object, so the code underneath doesn't complain.
And the above happens because initializeBean() delegates to the post processors, i.e. AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(). More specifically to PersistenceExceptionTranslationPostProcessor, which proxifies our bean because it sees @Repository
.
Attachments: - spring-bug-1.png (221.83 kB) - spring-bug-2.png (27.08 kB)
Comment From: bclozel
Working as designed. Bean cycles are also rejected in recent Spring Boot versions.