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.