Archie Cobbs opened SPR-9391 and commented
Spring has some very counter-intuitive behavior when handling prototype scoped and @Configurable
beans, or more generally, beans whose lifecycles are "smaller" than the lifecycle of the overall application context (e.g., that of singleton beans).
The issue is that initialization activity occurs for these beans (e.g., InitializingBean
) but destruction activity does not (e.g., DisposableBean
). For just one example, #11326 complains about this.
Related to this is the non-intuitive behavior that ApplicationListener
doesn't work at all for prototype and @Configurable
beans, presumably because there's no way for the application context to know when to unregister the listener (e.g., see the code quoted in #14023).
This contributes to much confusion about Spring. But even worse, the typical result for unaware programmers is memory leaks. This is due to destruction activity that never occurs, such as listeners that don't get unregistered, etc. In a servlet container environment, this can then cause class loader leaks on web application restart, etc.
All of these problems can be solved easily: have the application context maintain weak references to these beans, along with a corresponding reference queue, and invoke all of the destruction callbacks whenever any bean appears on the reference queue. In addition, when the application context itself is shutdown, since there can still be "live" prototype and @Configurable
beans, we need to find and destruct them; but these beans will exist as uncleared weak references, which can then be iteratively destructed (presumably we are discarding the weak references after destructing the corresponding bean, so at any given time the weak references we maintain represent only the "live" beans).
To avoid having to create a new thread to poll the reference queue, the application context can poll the reference queue every time a new prototype or @Configurable
bean is instantiated, etc. This ensures that there can be no memory leak caused by the maintenance of the references themselves. Regarding the behavior of destructor timing, in the worst case, beans will be always be destructed on application context shutdown, but typically much sooner. Better late than never!
FYI, this idea was originally mentioned in #9922; I'm just elaborating on it.
Affects: 3.1.1
Issue Links: - #14023 Add new method ConfigurableApplicationContext.removeApplicationListener() - #11326 Disallow destroy-method when scope is prototype - #9922 Prototype application context event listeners are not notified within the events
2 votes, 4 watchers
Comment From: spring-projects-issues
Archie Cobbs commented
One small additional comment... along with this change it might make sense to add a new method like AutowireCapableBeanFactory.destroyBean()
, which would complete the symmetry with AutowireCapableBeanFactory.configureBean()
and AutowireCapableBeanFactory.initializeBean()
.
This method would allow application code to forcibly destruct a bean instead of waiting for it to appear on the weak reference queue. In the implementation of this method, the corresponding weak reference would of course be removed to avoid double-destruction.
Comment From: spring-projects-issues
Bulk closing outdated, unresolved issues. Please, reopen if still relevant.
Comment From: archiecobbs
Bulk closing outdated, unresolved issues. Please, reopen if still relevant.
... except Github won't let me reopen it...
Comment From: jhoeller
After all these years, I do not see us revisiting the semantics of prototype beans anymore. While narrower scopes such as request, session or websocket are in common use with lifecycle management up until destruction, prototype beans are largely used according to their original semantics: creating bean instances which the factory is not responsible for managing any further after the initial hand-off.
Certain consumers of prototype beans may call ConfigurableBeanFactory.destroyBean
when they are done, triggering destruction callbacks (if any). Other consumers may be aware that their targeted beans do not need any destruction, creating many instances of those with an assumption that there is no further management necessary for them. Either way, the existing semantics seem to work for many scenarios.