Closed spring-projects-issues closed 6 years ago
Juergen Hoeller commented
This is not a new problem; it keeps coming up in some form every now and then. From that perspective, we should try to avoid such a hard IllegalStateException
and provide reasonable fallback behavior instead.
That said, the new factor here is Spring Boot: Its ConfigurationPropertiesBindingPostProcessor
rather aggressively builds its ConversionService
and therefore triggers the creation of Spring Data's converters, which implicitly leads to the publication of some events there.
So from my perspective, the problem is threefold:
Spring Boot shouldn't unconditionally trigger such early creation of Spring Data converters from within a BeanPostProcessor
which is even applied to very core beans such as MessageSource
that will be created before the ApplicationEventMulticaster
. That's "unfriendly behavior" for a bean post-processor.
Spring Data shouldn't try to publish events from within its AbstractMappingContext
, given that it may be called at such an early point. Or well, if it insists on doing that, it should guard its publishEvent
call with an IllegalStateException
catch clause, provided that it's ok for such an event to be ignored.
Spring's AbstractApplicationContext
currently initializes its MessageSource
bean before the ApplicationEventMulticaster
bean. The problem would largely go away if we inverted that order. However, a custom multicaster bean may also delegate to some other beans etc, so it wouldn't be completely safe.
A straightforward remedy would be to locally accumulate such early events, propagating them to the multicaster once initialized. I suppose the best place to do that would be AbstractApplicationContext
itself. However, we would only do that as of 4.2, so it still makes sense for Boot/Data to be more defensive there.
Juergen
Juergen Hoeller commented
I've implemented the approach I outlined, collecting those early events and publishing them in a batch once the multicaster is available. This definitely needs to go through the 4.2 release candidate phase though.
For 4.1.x, the easiest solution seems to be for Data/Boot to be more defensive there. I would recommend that in general, even against 4.2.
Juergen
Terence Mill commented
Similar things happen again on spring-cloud dalston ( spring framework 4.3.9.RELEASE)
java.lang.IllegalStateException: LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@624c96df: startup date [Thu Nov 02 08:27:24 CET 2017]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@1c5ebbc5
at org.springframework.context.support.AbstractApplicationContext.getLifecycleProcessor(AbstractApplicationContext.java:427)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:999)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:958)
at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at com.myorg.dvd.core.DvdSpringApplication.run(DvdSpringApplication.java:68)
at com.myorg.dvd.core.DvdSpringApplication.run(DvdSpringApplication.java:37)
at com.myorg.dvd.cfg.ConfigServerApplication.main(ConfigServerApplication.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
2017-11-02 08:27:28.425 ERROR [configserver,,,] 13740 --- [ restartedMain] o.s.b.f.s.DefaultListableBeanFactory : Destroy method on bean with name 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory' threw an exception
java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@624c96df: startup date [Thu Nov 02 08:27:24 CET 2017]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@1c5ebbc5
at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:414)
at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:253)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:961)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:968)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1030)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1006)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:958)
at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at com.myorg.dvd.core.DvdSpringApplication.run(DvdSpringApplication.java:68)
at com.myorg.dvd.core.DvdSpringApplication.run(DvdSpringApplication.java:37)
at com.myorg.dvd.cfg.ConfigServerApplication.main(ConfigServerApplication.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Juergen Hoeller commented
Terence Mill, indeed, this seems to be a related issue in ApplicationListenerDetector
which needs to defensively handle a call to the ApplicationEventMulticaster
for scenarios where the latter hasn't been initialized yet. Could you please create a separate JIRA ticket for this? I'll sort it out for 4.3.13 then.
Christopher Smith opened SPR-12902 and commented
I hit a bizarre bug in my application that has several StackOverflow and similar posts around the Web. Each time there's been an immediate workaround, but the behavior smells like a bug in the DI dependency algorithms, and I wanted to raise it for review.
The problem manifests when a component used by MVC, such as a
Converter
or argument resolver, depends on a Spring Data repository. Attempting to create the component's bean throws anIllegalStateException
with the messageRemoving the dependency on the repository bean or the
implements
clause that makes it interesting to MVC makes the exception go away. Injecting via field or constructor makes no difference. Creating the bean via component scanning or an@Bean
method (with parameter or configuration-field injection) makes no difference. The issue has been seen with JPA, MongoDB, and Neo4J repositories.Workarounds have suggested "use the latest snapshot of Spring library $FOO", but I believe this is masking the bug: I have sandbox Spring Boot launchers for my (multi-module) core Maven project and my (multi-module) application project that depends on it; all of the Spring dependencies are of identical versions, and only the application launcher throws the exception while the core launcher starts normally. Both are obtaining the MongoDB configuration from a common
CloudConfig
class in the core project, using the same properties file for connection parameters.This appears to be a fairly nondeterministic failure, and the apparent sensitivity on irrelevant details like the existence of extraneous beans makes me suspect that the problem is in the DI container, but it might be a bug in the Spring Data mapping code.
Similar reports:
Stack trace:
Trivial component class triggering the exception:
List of dependencies for working core launcher:
Dependencies for broken application launcher:
Note: I have found that annotating the injection point with
@Lazy
seems to work as a workaround, but since nothing explicitly depends on this bean (and if it did, the exception should be one for a circular dependency), eager initialization should work.Affects: 4.1.4
Issue Links:
20697 Early ApplicationContext close call may lead to ApplicationEventMulticaster/LifecycleProcessor access exception
19689 Annotation-based event listener does not receive event published in
@PostConstruct
Referenced from: commits https://github.com/spring-projects/spring-framework/commit/9ed0a56d840886e4d6d9d90a4e21066c789f128e