BadPractice9 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki
Bug Pattern Type: DESTRUCTION_METHOD_DEFINED_ON_PROTOYPE_SCOPED_BEAN Short Description: Do not desire the destruction process in the prototype-scoped bean.
Description
The life cycle of a bean goes through some steps: bean instantiation, dependency injection, bean creation life-cycle callback, and bean destruction life-cycle callbacks. The purpose of the final step is to shut down gracefully. During the step, Spring will not call the destruction method for those beans with prototype scope (Spring framework defines 6 types of scopes:singleton, prototype, request, session, application, and websocket). Thus, destroy definitions such as destroy()
in a bean + destroy-method
attribute in an XML configuration file would not be called.
// Bean scope definition examples
// Case 1 - in configuration class
class AppConfig{
@Bean
@Scope("prototype")
public IBeanA beanA() {
return new BeanA();
}
}
// Case 2 - at class level
@Scope("prototype") // @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) constant can be used as well
@Component
class BeanA{
}
// Case 3 - XML configuration
<bean id="beanA" class="com.~.BeanA" scope="prototype"/>
Evidence
(Write more on - There are two types of destruction: Manual destruction and Automatic destruction link) AS can be seen below, Spring code specified that only singletons are targets for the destruction cycle.
//---------------------------------------------
@Override
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// ...
}
}
//---------------------------------------------
/**
* Actually performs context closing: publishes a ContextClosedEvent and
* destroys the singletons in the bean factory of this application context.
* ...
*/
protected void doClose() {
//...
destroyBeans();
//...
}
//---------------------------------------------
protected void destroyBeans() {
getBeanFactory().destroySingletons(); // Only singleton beans are destroyed
}
Implement Strategy
Since there are some ways to hook into bean destrcution: destroy() methods in bean configuration file, DisposableBean
callback interfaces, and @PreDestroy
annotations (see cases below), the detector should include the case of destruction hooking mechanisms and also consider not only the XML (<bean id="" class="" destroy-method="destroy">
) but also Java configuration (e.g., @Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy")
).
Case 1
<bean id="destructiveBean"
class="com.apress.prospring5.ch4.DestructiveBean"
destroy-method="destroy"/>
public class DestructiveBean {
public void destroy() {
// release resource
}
}
Case 2
<bean id="destructiveBean"
class="com.apress.prospring5.ch4.DestructiveBeanWithInterface" />
public class DestructiveBeanWithInterface implements DisposableBean {
@Override
public void destroy() {
// release resource
}
}
Case 3
public class DestructiveBeanWithJSR250 {
@PreDestroy
public void destroy() {
// release resource
}
}
Case 4 - Java configuration
// Add also <context:annotation-config/> in XML configuration.
@Configuration
class DestructiveBeanConfig {
@Lazy
@Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy")
DestructiveBeanWithJSR250 destructiveBean() {
DestructiveBeanWithJSR250 destructiveBean = new DestructiveBeanWithJSR250();
return destructiveBean;
}
}
Reference List
- Pro Spring 5, Ch 4 - section of "Hooking into Bean Destruction" (139 page)
- https://stackoverflow.com/questions/50681027/do-spring-prototype-beans-need-to-be-destroyed-manually
- https://howtodoinjava.com/spring-core/spring-bean-life-cycle/
- https://www.baeldung.com/spring-bean-scopes
- https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch04s04.html
- https://programmersought.com/article/75127516273/