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:
- 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 - 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>
- 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
- 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:
- method from annotation (@PostConstruct e @PreDestroy)
- method from interfaces (InitializingBean e DisposableBean)
- 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.
Leave a Reply