51

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.

skwisgaar
  • 880
  • 2
  • 13
  • 31
Ignasi
  • 5,887
  • 7
  • 45
  • 81

3 Answers3

50

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.

Ignasi
  • 5,887
  • 7
  • 45
  • 81
  • Hey, can you explain on the part : "it detects that autowired C into B and A is not the same as proxy of C" – Sachin Sharma May 16 '16 at 06:36
  • 2
    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. – Ignasi May 17 '16 at 16:10
  • 1
    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? – Sachin Sharma May 18 '16 at 06:08
  • So `@Lazy` `@Autowired` the service into the other services got me the wanted result. – Philip Aug 30 '19 at 13:04
21

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.

Vikash Singh
  • 261
  • 2
  • 7
12

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

masT
  • 804
  • 4
  • 14
  • 29