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