spring autowired aop circular dependency
Asked Answered
B

4

52

I'm using java config with @ComponentScanin order to initialize my beans and @EnableAspectJAutoProxy(proxyTargetClass=true)to use cglib proxies.

In this project we have a lots of generated services autowired between them using @Autowired. It works pretty well.

But, for some of these services I've added @Async (I've also added @EnableAsync(proxyTargetClass = true)on my @Configurationclass).

After that, I'm getting:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'ConversationUserLocalService': Bean with name 'ConversationUserLocalService' has been injected into other beans [ConversationUserHistoryLocalService] i
n 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 'a
llowEagerInit' flag turned off, for example.

I guess this is because Spring is injecting the service with @Asyncmethod BEFORE AOP creates the proxy. Could this be the problem? How I should fix it?

In order to try to clarify my problem, let's say I have:

@Service A, B & C;

A has autowired B & C, B has autowired A & C, C has autowired A & B;

C has a method marked as @Async.

When Spring initialize applicationContext, it tries to initialize A, but needs B & C, so it initializes them. But after all, AOP tries to make a proxy of C (because @Async) and then it detects that autowired C into B and A is not the same as proxy of C so it fails.

I hope this can explain a little more what is happening.

Barnes answered 11/3, 2015 at 11:15 Comment(1)
Please post a SSCCEWilona
E
13

AsyncConfigurer configuration classes get initialized early in the application context bootstrap. If you need any dependencies on other beans there, make sure to declare them @Lazy as far as possible in order to let them go through other post-processors as well.

Reference JavaDoc: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html

Elveraelves answered 21/5, 2019 at 8:40 Comment(0)
B
51

Finally I sorted it out using @Lazyon services (with methods annotated with @Async), and also, where they were autowired. This way I guess Spring only initialize and autowires those services when they're required instead of on application context initialization.

Barnes answered 11/3, 2015 at 18:28 Comment(4)
Hey, can you explain on the part : "it detects that autowired C into B and A is not the same as proxy of C"Saville
When Spring creates a proxy to add async behavior into C it doesn't updates references on A and B. So both of them has a reference to the raw version of C.Barnes
thanks for the explanation. just one more thing, will this depend on the order in which spring initializes the services. In your example, if spring initialized C first even then the same exception will occur?Saville
So @Lazy @Autowired the service into the other services got me the wanted result.Grubb
D
23

I had same issue and I solved this issue:

  1. I identified which @Autowired property is reason for circular dependency.

    Eg:

    @Autowired
    private TestService testService;
    

    (Tips to identified just try to comment and find out which property is reason to break the application)

  2. Once identified just use @Lazy on top of this @Autowired variable.

    Eg :

    @Lazy
    @Autowired
    private TestService testService;
    

    And Application worked smoothly.

Dulosis answered 16/4, 2020 at 13:49 Comment(0)
E
13

AsyncConfigurer configuration classes get initialized early in the application context bootstrap. If you need any dependencies on other beans there, make sure to declare them @Lazy as far as possible in order to let them go through other post-processors as well.

Reference JavaDoc: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html

Elveraelves answered 21/5, 2019 at 8:40 Comment(0)
R
0

For me, it was impossible to add @Lazy above all existing @Autowires of an existing Service, as they were more than 100. So, by creating a new Service, moving there all methods requiring @Async decorator, and then Autowiring this new Service when needed (without @Lazy), works perfectly.

Rakeoff answered 30/1 at 6:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.