I am working on a Jakarta EE application that uses Spring Framework (not Spring Boot). After upgrading the spring-framework-bom version from 6.1.16 to 6.2.x, the following issues have emerged:
- Issue with injecting generic beans. Some beans defined with generics are no longer injected correctly. For example:
@Bean
public MyInterface myBean() {
return new MyClass<>();
} // raw type definition
bean injected as follows:
@Qualifier("myBean")
@Autowired
private MyClass<MyObject> myBean; // MyClass implements MyInterface
This generates an exception stating that no bean of type MyClass
-
Random errors during deployment. In some cases, the application deployment fails with the following exception: Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'exampleBean': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
The issue is non-deterministic: in subsequent deployments, the exception is reported for different beans. I have verified that these issues have been reported and tracked in the official Spring Framework tickets: https://github.com/spring-projects/spring-framework/issues/34271 and https://github.com/spring-projects/spring-framework/issues/34186
-
Java Config injection sometimes fails. In certain cases, the injection of a Java Config class fails. For example:
@Autowired
private MyJavaConfig myJavaConfig;
@Bean
public MyBuilder myBuilder() {
return new MyBuilder(this.myJavaConfig.serviceFactory());
}
This generates the following exception: Caused by: java.lang.NullPointerException: Cannot invoke \"MyJavaConfig.serviceFactory()\" because \"this.myJavaConfig\" is null
These issues occur randomly, and I have not been able to reproduce them in a greenfield project. However, reverting to version 6.1.16 of Spring Framework resolves the deployment issues.
Has anyone encountered similar problems with Spring Framework 6.2.x, or does anyone have suggestions on how to address these cases?
Comment From: snicoll
What version are you using. You mentioned 6.2.x but not the actual version. 1 and 2 looks duplicate of already raised concerns that are fixed in 6.2.2
.
Also please do not create "meta-issue" like this. For each problem, search first in the issue tracker if an issue has already been raised.
Comment From: gmarfia88
The latest tests were done with version 6.2.2 but I confirm the problems reported in points 2 and 3. (For point 1 I will probably have to check again on 6.2.2).
Comment From: bclozel
2) already got an extensive comment from Juergen explaining the situation. Please apply the advice in your application.
As for 3), your configuration has been relying on an unspecified order and worked by accident. Declaring dependencies is the expected way:
@Bean
public MyBuilder myBuilder(MayJavaConfig myJavaConfig) { return new MyBuilder(myJavaConfig.serviceFactory()); }
I'm closing this issue for now. We can reopen it if you provide a sample application that uses Spring Framework 6.2.2 and reproduces the problem.
Comment From: gmarfia88
Hi, I am commenting on the ticket because the issue with circular dependencies occurs in a single-threaded context. I do not have any custom configurations for multi-threading, and, as far as I know, Spring by default does not use multiple threads for context initialization. Could you please confirm if this statement is correct?
Regarding point 3, I would like to emphasize that my application has been working correctly for years with the current configuration. The suggestion you proposed would force me to modify thousands of configurations due to a minor update (from 6.1.16 to 6.2.x) of Spring Framework.
Moreover, by injecting a Java Config via @Autowired, I can define a bean without needing to pass a parameter as input, as shown in the following example:
public interface ConfigInterface { MyBuilder myBuilder(); }
@Configuration
public class ConfigA implements ConfigInterface {
@Autowired private MyJavaConfig myJavaConfig;
@Bean @Override public MyBuilder myBuilder() { return new MyBuilder(this.myJavaConfig.serviceFactory()); }
}
@Configuration
public class ConfigB {
@Autowired private ConfigInterface configInterface;
@Bean public MyBean myBean() { return new MyBean(this.configInterface.myBuilder()); }
}
If I were to follow your suggestion and add the configuration as a bean parameter, I would also need to inject MyJavaConfig, which would further complicate the management:
@Configuration
public class ConfigA implements ConfigInterface {
@Bean @Override public MyBuilder myBuilder(MyJavaConfig myJavaConfig) {
return new MyBuilder(myJavaConfig.serviceFactory());
}
}
@Configuration
public class ConfigB {
@Bean
public MyBean myBean(ConfigInterface configInterface, MyJavaConfig myJavaConfig) {
return new MyBean(configInterface.myBuilder(myJavaConfig));
}
}
This approach could result in a less clean design, as I might not want to expose MyJavaConfig to ConfigB.
Comment From: bclozel
I am commenting on the ticket because the issue with circular dependencies occurs in a single-threaded context. I do not have any custom configurations for multi-threading, and, as far as I know, Spring by default does not use multiple threads for context initialization. Could you please confirm if this statement is correct?
This is correct, Spring Framework does not use multiple threads by default for context initialization. You can confirm whether this is the case in your application by applying the advice from this comment: https://github.com/spring-projects/spring-framework/issues/34308#issuecomment-2609997613
Regarding point 3, I would like to emphasize that my application has been working correctly for years with the current configuration. The suggestion you proposed would force me to modify thousands of configurations due to a minor update (from 6.1.16 to 6.2.x) of Spring Framework.
Apologies, I didn't realize that your MyJavaConfig
was an actual @Configuration
class. I think that autowiring a configuration class into another is a major design issue in the first place. As you've pointed out, this has been working for a long time anyway so we'll be waiting for your analysis on the probable multi-threaded initialization.
Comment From: jhoeller
@gmarfia88 to be clear, if you encounter an unexpected BeanCurrentlyInCreationException
during single-threaded startup just going from 6.1.x to 6.2.x, that would qualify as a bug that we would definitely like to have a reproducer for. That said, since it is non-deterministic, it is highly likely that there is another thread involved during startup; we would like to learn about where that is being started so that we can fine-tune the locking algorithm for 6.2.3 if possible.
The config class injection failure may be a consequence of circular reference resolution. So this may possibly be connected to the problem above, with the container trying to resolve a BeanCurrentlyInCreationException
through a fallback and failing there. I would not worry about that part too much at this point, there is no need to revisit your config classes. Let's try to track down the BeanCurrentlyInCreationException
scenario first and then see whether a remaining problem persists for config classes specifically.
Your type matching problem is tied to a different area of fine-tuning. We've fixed several generic type matching regressions in 6.2.2 already and are about to address one more in 6.2.3. A reproducer against 6.2.2 would be very much appreciated there, ideally attached to a new issue for that specific problem.
Comment From: gmarfia88
Hi @jhoeller, I have probably identified a part of my application that uses a thread pool to instantiate some beans. As a result (although I am not yet completely sure), the context loading might be happening in a multi-threaded manner.
I agree that we should not worry for now about the issue regarding configuration injection and generics, but instead focus on understanding and resolving the BeanCurrentlyInCreationException. Unfortunately, I need some time to figure out how to postpone the initialization of those beans or, in general, how to bypass the issue to verify that, once this is fixed, everything works correctly again.
As soon as possible, I will try to detail the bean creation logic in my scenario so that you can evaluate possible improvements for the next version.
Thanks a lot!
Comment From: gmarfia88
Hi, I confirm that I am experiencing the same situation as described in issue 34308
In my case, I am using Pekko, which relies on a thread pool. To correctly create the actors, I request beans from Spring's ApplicationContext through these threads.
Despite trying to comment out the problematic code section, I am still encountering issues with injecting beans that use generics. The error I receive is as follows:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myConfig': Unsatisfied dependency expressed through field 'myBean': No qualifying bean of type 'MyInterface
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'MyInterface
This issue occurs because myBean is defined in another configuration as a raw type, rather than using the interface with generics. Here's an example of the current definition:
@Bean
public MyClass myBean() { return new MyClass<>(); }
Do you think these issues could be resolved in the upcoming framework patches?
Thanks a lot!
Comment From: jhoeller
@gmarfia88 for the bean type matching, could you try the latest 6.2.3 build snapshot? This fixes a couple of reported regressions in the generic type matching algorithm in 6.2.
That said, we are actually not aware of a specific regression with generics versus raw classes. If you keep seeing this, you could try to isolate it into a unit test that passes on 6.1.x and fails against 6.2.x.
Also, the return type of a @Bean
method should usually be declared as specific as possible - ideally the actual implementation type -, in particular if injection points happen to refer to it by the implementation type.
Comment From: jhoeller
@gmarfia88 for the Pekko part, where is the Pekko subsystem being started? I assume this is some special Spring bean declaration that exposes the actor system and internally starts the actor thread pool? If this is the case, you should make sure for the actor bean to be initialized as late as possible.
Ideally such actor bootstrapping should happen in a SmartInitializingSingleton.afterSingletonsInstantiated
method or in a ContextRefreshedEvent
listener. If it needs to happen as a regular Spring @Bean
method, you could try to enforce the early initialization of the common beans that those actors need through a corresponding @DependsOn
declaration on the actor @Bean
, enforcing their early initialization within the startup lock.
Alternatively, you could also mark those common beans (the ones that you are seeing a BeanCurrentlyInCreationException
for) as @Lazy
so that the main bootstrap thread skips their initialization within its startup lock, leaving their initialization entirely up to actor threads (effectively avoiding the threading conflict as well).
Maybe we can fine-tune 6.2's startup behavior with fallbacks at runtime that can handle such multi-threading conflicts still. Even if so, depends-on declarations as suggested above will help for clarity and for a better controlled bootstrap phase.
Comment From: jhoeller
On a related note, I'm about to bring #34349 into the next 6.2.3 snapshot. This ignores BeanCurrentlyInCreationException
on mainline pre-instantiation, silently accepting that some other thread will finish initializing such a bean concurrently. This might help for your actor scenario; please give the upcoming 6.2.3 snapshot a try for that reason as well. That said, I still recommend starting the actor subsystem as late as possible - and to express async initialization dependencies through @DependsOn
so that such common beans get initialized in the main bootstrap thread first - in order to avoid such unmanaged concurrent initialization as far as possible.
Comment From: gmarfia88
Hi @jhoeller, I tried using version 6.2.3-SNAPSHOT of spring-framework-bom, but the issues persist.
I’m working on some modifications to the Pekko part, but I haven’t found a definitive solution yet - it will take some more time.
The initial changes I made allowed me to proceed with the deployment, but I encountered the same issues mentioned in my first message:
- Issue with injecting generic beans
- Java Config injection sometimes fails
I'm not sure if a permanent modification to the Pekko part will resolve these issues, but I can confirm that over the years, up until version 6.1.x, I had never encountered any.
Thanks for your support.
Comment From: jhoeller
@gmarfia88 thanks for following up. Your scenario is the last remaining case where we are aware of regressions in 6.2, so let's try to get to the bottom of it.
For the generics part, I'll be happy to debug it if we can narrow it down a reproducer. Ideally a unit test in the style of our ResolvableTypeTests
but a minimal application context setup where injection fails accordingly would also help.
As of 6.2.3, the generic matching regressions have been fixed as far we understand them. It's generally recommendable to define a MyClass<>
return type rather than relying on raw type matches - but even the raw matching fallback keeps working according to our unit tests. I am unfortunately out of educated guesses for where a remaining problem could emerge here.
As for the concurrent initialization part, I still see some potential for refinements based on the specific thread interactions we are encountering at runtime. If necessary, we could even have an application-wide flag to opt out of lenient locking, restoring pre-6.2 locking behavior for specific applications (always picking the hard-locking code path, never attempting lenient locking).
Comment From: gmarfia88
Hi @jhoeller, I had already tried to replicate the bean issue in a from scratch project but without success.
The possibility of having an application-wide flag would be really useful, as it could help us understand if the other issues are also related to the bean instantiation done by Pekko threads.
Let me know and thanks!
Comment From: jhoeller
@gmarfia88 the upcoming 6.2.3 snapshot has another revision which waits for leniently created beans in the mainline thread, largely restoring the behavior that we had in 6.1.x without enforcing the common singleton lock in all threads. Please give this another try!
With this latest change in place, I don't see a need for an application-wide flag anymore. This should work by default in all cases now.
Comment From: gmarfia88
Hi @jhoeller, I just ran another test with version 6.2.3-SNAPSHOT, but I'm still encountering the same issues.
Currently, the deployment fails due to a config class injection failure.
Comment From: jhoeller
@gmarfia88 thanks for the immediate feedback!
I'm rather surprised that you are still encountering a BeanCurrentlyInCreationException
against the latest 6.2.3 snapshot since this is pretty comprehensively handled for all common scenarios now (with various lock/wait variants, also verified in tests), so I'm really not seeing the potential for it anymore. Could you share a specific stacktrace for a BeanCurrentlyInCreationException
that you encountering at this point, ideally combined with debug-level logging right before that exception?
As for generics matching, all specifically known regressions have been addressed. Without a reproducer for that part, we're out of luck in terms of further investigation.
Comment From: jhoeller
For reference, this is the common test class for multi-threaded bootstrap scenarios: https://github.com/spring-projects/spring-framework/blob/main/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java
Also, please double-check that your 6.2.3 snapshot has actually been refreshed from the repo (not turned stale locally).
Comment From: jhoeller
An update: I've managed to still arrive at a BeanCurrentlyInCreationException
with a high enough number of concurrent threads trying to do the same initialization steps. Interestingly, 2 threads are not sufficient; I can only get it to fail with 4 threads or more. I'll debug locally, there is no immediate need to provide a debug log from your side.
I'll reopen #34349 accordingly. There might a new 6.2.3 snapshot ready for testing tomorrow then :-)
Comment From: jhoeller
And another update right away: It turns out that there was still a problem when multiple custom threads where competing for the initialization of the same transitive beans - so not competing with the mainline bootstrap thread (which we tend to be focused on) but actually competing among each other. This was easy enough to fix by tweaking the lock/wait condition and will be available in the upcoming 6.2.3 snapshot within half an hour.
For the generics problem, I strongly recommend making your return type declaration as specific as possible. This should address the immediate issue at hand but it's not meant as a workaround, it's the recommended way of declaring it in general.
Thanks for staying on board with this effort :-) Note that we intend to release 6.2.3 in the course of tomorrow, so it'd be great if you could re-test it before the release still.
Comment From: gmarfia88
Hi @jhoeller, I removed the org/springframework folder from my .m2 directory, so I should have downloaded the correct version. I'm using this repository:
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
As I mentioned, the error I encountered yesterday was due to the injection of a null Java Config, so it wasn't directly related to a BeanCurrentlyInCreationException.
In my case, I have at least one Pekko thread pool configured with a minimum size of 16 threads.
I'm not sure if I can proceed with a test yet. Let me know, and thanks for your support!
Comment From: jhoeller
@gmarfia88 ok thanks for double-checking!
So for the Java config injection part, we still have no idea what this is caused by at all. Is this reproducible on every deployment or rather a race condition that does not happen every time?
That said, for a start, it would be a huge improvement already if this was the only issue left. Anything you could do to see whether the BeanCurrentlyInCreationException
problem is addressed by comparing 6.2.2 with 6.2.3 snapshot in a Pekko setup would be beneficial, even if not directly inferred from your full application.
Comment From: gmarfia88
Sorry @jhoeller, but I'm not sure if there have been any further changes to version 6.2.3-SNAPSHOT since yesterday. Should I run the test again?
Comment From: jhoeller
@gmarfia88 the latest is from 23:30 last night. This is definitely worth re-testing since it addresses even scenarios with a high number of threads for me locally. I cannot reproduce any BeanCurrentlyInCreationException
against this anymore, with any number of threads.
Comment From: gmarfia88
Ok, I'll run the test again and update you as soon as possible.
Comment From: gmarfia88
Hi @jhoeller, Unfortunately, I'm still getting the same error. I'm sharing an additional part of the stacktrace:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [myDao]: Circular reference involving containing bean 'myDaoConfig' - consider declaring the factory method as static for independence from its containing instance. Factory method 'myDao' threw exception with message: Cannot invoke \"MyJavaConfig.serviceFactory()\" because \"this.myJavaConfig\" is null
Caused by: java.lang.NullPointerException: Cannot invoke \"MyJavaConfig.serviceFactory()\" because \"this.myJavaConfig\" is null"}}
I assure you that with version 6.1.16, the deployment of the same code works correctly.
Comment From: jhoeller
@gmarfia88 I'm not surprised about that part, although we still need to get to the bottom of what this is actually about. It seems to be involving a circular reference where resolution can be non-deterministic: concretely, it looks like a circular reference between two of your configuration class instances. Generally, we recommend avoiding such circular references since they are inherently brittle. Depending on initialization order, they can lead to different outcomes (or work in one bootstrap but fail in the next attempt). This is particularly brittle in multi-threaded bootstrap scenarios where there is no deterministic initialization order by design.
Moving that injection to a factory method argument would avoid that circular reference already, redeclaring your method as follows and removing the @Autowired
field completely:
@Bean public MyBuilder myBuilder(MyJavaConfig myJavaConfig)
Or alternatively, declaring the injected field and all similar fields in your related configuration classes as lazy (although I actually recommend the factory method argument style above):
@Autowired @Lazy private MyJavaConfig myJavaConfig;
For today's purposes, it'd be great to double-check BeanCurrentlyInCreationException
specifically. Could you possibly patch the case above so that you could test whether there are still any BeanCurrentlyInCreationException
cases in the Pekko bootstrap?
Comment From: gmarfia88
Hi @jhoeller, I would rule out the first solution, as I already described the details here.
As for the second one, I can try, but I'm not sure we'll be able to make it work. My application is huge, and I don't know how many changes I'll need to make before I manage to fix everything. I can tell you that, at first glance, it doesn't seem like there's a real circular dependency, especially since I have an integration test where I set setAllowCircularReferences(false) on the AnnotationConfigWebApplicationContext, and it always passes.
I agree that the bean loading order is non-deterministic, but the deployment has worked this way for years on different hardware and operating systems, so I don't know why it's happening now. Until version 6.1.x, we seemed to be protected from this issue.
Comment From: jhoeller
@gmarfia88 point taken, I understand that this is not really feasible for a large existing application codebase. We should be able to find out what actually changed from 6.1.x and 6.2.x, however, that part is quite a mystery still. It'd be my pleasure to get to the bottom of this - it's just out of scope for the 6.2.3 release later today, I'm afraid.
So how did you encounter the BeanCurrentlyInCreationException
cases that you initially mentioned in the issue description? Any indication whether they still occur where you previously noticed them?
Comment From: gmarfia88
@jhoeller I tried adding @Lazy to that configuration. The error then shifted to another one, but even after adding @Lazy again, I still get a NullPointerException in the same place. So, the first time it seems to shift the problem, but the second time it doesn't.
Comment From: gmarfia88
Hi @jhoeller, do you have any updates? Do you think it would be possible to reconsider using an application-wide flag to restore the behavior prior to 6.2?
Let me know, thanks!
Comment From: jhoeller
@gmarfia88 to be clear, such an application-wide flag (as suggested before) would only have covered the BeanCurrentlyInCreationException
problem - which, as far as I can tell, has been comprehensively addressed for 6.2.3 and reported to work for other scenarios already.
From that perspective, such a flag would not solve your remaining problems: the configuration class empty field problem and the raw type matching problem. Those are independent and will require specific solutions that we cannot do as an application-wide flag. Without being able to locally reproduce those specific problems, we cannot make progress there either, I'm afraid.
Comment From: gmarfia88
Hi @jhoeller, sorry for the delay.
I was thinking again about the flag because it’s not clear to me whether the case where the config is injected as null is actually due to that specific configuration being initialized by a Pekko thread. I thought the flag could be a good way to confirm or disprove this hypothesis.
Thanks again for your support.
Comment From: gmarfia88
Hi @jhoeller, sorry for the delay, here’s an update on the current status.
To run a test, I commented out part of the code and postponed the execution of some sections (all related to Pekko). By doing so, I managed to deploy the application, confirming that the issue is exclusively tied to the instantiation phase triggered by Pekko's thread pools. I tested this with version 6.2.4 of spring-framework-bom.
However, at this stage, I’m unable to create a green field PoC that replicates my application's configuration due to its complexity. I will try to reimplement certain parts, but for a minor upgrade from 6.1.x to 6.2.x, this impact is quite significant.
Comment From: jhoeller
That's some interesting insight indeed, thanks for trying it. So these are all side effects of concurrent bean initialization - that's surprising but very useful to know. I suspect that the increased concurrency changes the effective order of bean initialization in a way that breaks your scenario (running into circular-reference-like cases), whereas the previous strict locking (effectively serializing bean initialization even in multi-threaded scenarios) seems to have produced an order of initialization that works for your application.
Let's try to sort this out for 6.2.6 in April, one way or the other. If we can't find any other solution, maybe it'll have to be an application-wide flag indeed then. At this point we are not aware of another Pekko application that breaks but that might be because those are simpler in terms of their structure, leading to an effective initialization order that still does not break in a concurrent case.