Categories
Java

Executing code on the creation or destruction of a bean in Spring

There are 3 ways to execute code to create a bean in Spring:

  • with the annotation @PostConstruct
  • implementing the interface InitializingBean
  • configuring the property init-method

The reason why you run code when you create a bean is often to check the dependencies, and assign a default value, or automated actions such as starting a scheduled task.
The constructor of the bean may not be suitable for this purpose because the bean is not fully initialized until the constructor has not completed.

In a similar way there are 3 ways to execute code in the destruction of a bean in Spring:

  • with the annotation @PreDestroy
  • implementing the interface DisposableBean
  • configuring the property destroy-method

Usually these methods are used to free resources such as open files but notice that they are only called for singleton bean (single instance of the bean in the application); in the case of prototype bean only the destroy method of the DisposableBean interface is called.
In any case you need an explicit call to a destroy method (of AbstractApplicationContext or the bean), it is not enough that the application simply ends.
An alternative is to use the method AbstractApplicationContext.registerShutdownHook() with which you create a thread just before closing the application that calls the destroy method of AbstractApplicationContext.
The following table shows all possibilities:

singleton bean prototype bean
AbstractApplicationContext.destroy() @PreDestroy
DisposableBean.destroy()
destroy-method
no methods
bean.destroy() DisposableBean.destroy() DisposableBean.destroy()
AbstractApplicationContext.registerShutdownHook() @PreDestroy
DisposableBean.destroy()
destroy-method
no methods

Of course, not all methods must be implemented at the same time but you can choose which to use.

pro con
methods configured in file .xml decoupled from Spring manual configuration of every bean
methods inherited from interfaces configuration of only one class, portability coupled to Spring
methods with annotation portability required support for JSR-250

To test this, create a new java project:

  1. create the file conf/app-context.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
        default-lazy-init="true">
    
    	<context:annotation-config/>
    
        <bean id="simpleBean" class="eu.lucazanini.mybean.SimpleBean" 
    	scope="singleton"
        init-method="initMethod"
        destroy-method="destroyMethod"/>
    
    </beans>
    

    the directory “conf” must be in the classpath;
    you need the row <context:annotation-config/> if you use the annotations

  2. create the file conf/log4j.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">
    
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 
        <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d %-5p %c{1}:%L %m %n" />
    <!--
    ConversionPattern format specification
    %d      inserts the date; you can specify the format (%d{yyyy-MM-dd HH:mm:ss,SSS})
    %-5p    inserts the priority log level, 5 characters, left justified
    %c{1}   inserts the name of the class
    %L      inserts the line number
    %m      inserts the user message
    %n      inserts the separator (for example, a new line)
    -->
            </layout>
        </appender>
    
        <appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
        	<param name="append" value="false" />
            <param name="Threshold" value="DEBUG" />
            <param name="File" value="logs/app.log"/>
            <param name="MaxFileSize" value="512KB" />
            <param name="MaxBackupIndex" value="10" />
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d %-5p %c{1}:%L %m %n" />
            </layout>
        </appender>
    
    <!--sets the priority log level for org.springframework-->
        <logger name="org.springframework">
            <level value="info"/>
        </logger>
    
    <!--sets the priority log level for eu.lucazanini-->
        <logger name= "eu.lucazanini">
            <level value="debug"/>
        </logger>
    
    <!--sets the default priority log level-->
        <root>
            <priority value="debug"></priority>
            <appender-ref ref="stdout"/>
            <appender-ref ref="fileAppender"/>
        </root>
    </log4j:configuration>
    
  3. create the file eu/lucazanini/mybean/Main.java
    package eu.lucazanini.mybean;
    
    import org.apache.log4j.Logger;
    import org.springframework.context.support.*;
    
    public class Main {
    
        private static org.apache.log4j.Logger log = Logger.getLogger(Main.class);
    
        public static void main(String[] args) {
    
    	GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
    	ctx.load("classpath:app-context.xml");
    	// ctx.registerShutdownHook();
    	ctx.refresh();
    
    	log.debug("creating bean");
    	SimpleBean bean = (SimpleBean) ctx.getBean("simpleBean");
    	log.debug("created bean");
    
    	log.debug("destroying bean");
    	ctx.destroy();
    	// try {
    	// bean.destroy();
    	// } catch (Exception e) {
    	// e.printStackTrace();
    	// }
    	log.debug("destroyed bean");
    
    	log.debug("application shutdown");
        }
    }
    

    the class GenericXmlApplicationContext is a subclass of AbstractApplicationContext

  4. create the file eu/lucazanini/mybean/SimpleBean.java
    package eu.lucazanini.mybean;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    
    public class SimpleBean implements InitializingBean, DisposableBean {
    
        private static org.apache.log4j.Logger log = Logger
    	    .getLogger(SimpleBean.class);
    
        public SimpleBean() {
    	log.debug("constructor");
        }
    
        @PostConstruct
        public void postConstructMethod() {
    	log.debug("@PostConstruct");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
    	log.debug("InitializingBean.afterPropertiesSet()");
        }
    
        public void initMethod() {
    	log.debug("init-method");
        }
    
        @PreDestroy
        public void preDestroy() {
    	log.debug("@PreDestroy");
        }
    
        @Override
        public void destroy() throws Exception {
    	log.debug("DisposableBean.destroy()");
        }
    
        public void destroyMethod() {
    	log.debug("destroy-method");
        }
    
    }
    

The output is the following:

...
DEBUG Main:17 creating bean
DEBUG SimpleBean:16 constructor
DEBUG SimpleBean:21 @PostConstruct
DEBUG SimpleBean:26 InitializingBean.afterPropertiesSet()
DEBUG SimpleBean:30 init-method
DEBUG Main:19 created bean
DEBUG Main:21 destroying bean
...
DEBUG SimpleBean:35 @PreDestroy
DEBUG SimpleBean:40 DisposableBean.destroy()
DEBUG SimpleBean:44 destroy-method
DEBUG Main:28 destroyed bean
DEBUG Main:30 application shutdown

See that the order in which these methods are executed is fixed:

  1. method from annotation (@PostConstruct e @PreDestroy)
  2. method from interfaces (InitializingBean e DisposableBean)
  3. method from file .xml (init-method e destroy-method)

if you comment and uncomment some parts of the file Main.java or replacing “singleton” with “prototype” as scope of the bean in app-context.xml, you can get the other output shown in this post.

One reply on “Executing code on the creation or destruction of a bean in Spring”

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.