Spring @PostConstruct and @PreDestroy Example

In this tutorial I will show you how we can use @PostConstruct and @PreDestroy annotation in Spring framework. @PostConstruct and @PreDestroy annotations are generally considered best practices for receiving life cycle callbacks in a modern Spring application. Using these annotations means that our beans are not coupled to Spring specific interfaces.

Sometimes we need to load some data or perform initialization work after all necessary properties on the bean have been set by the container.

In this example we will load data from database during application startup and set that value to a System property so that the value will be set only once and that value can be used later after retrieving from the System property.

Prerequisites

Eclipse 2019-12, Java at least 1.8, Spring Boot 2.3.1, MySQL 8.0.17

Setup Project

Create either gradle or maven based project in Eclipse. The name of the project is spring-postconstruct-predestroy.

If you create gradle based project then use below build.gradle script:

buildscript {
	ext {
		springBootVersion = '2.3.1.RELEASE'
	}
	
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
	mavenLocal()
    mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter:${springBootVersion}")
	implementation("org.springframework.boot:spring-boot-starter-jdbc:${springBootVersion}")
	implementation('mysql:mysql-connector-java:8.0.17')
	
	//required for jdk 9 or above
	runtimeOnly('javax.xml.bind:jaxb-api:2.4.0-b180830.0359')
}

If you create maven based project then use below pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-postconstruct-predestroy</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.1.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.17</version>
		</dependency>
		
		<!--required only if jdk 9 or higher version is used-->
		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.4.0-b180830.0359</version>
		</dependency>
	</dependencies>

    <build>
        <plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>at least 8</source>
					<target>at least 8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

MySQL Table

To query MySQL database we need to create a table under roytuts database as shown below:

CREATE TABLE `flag` (
  `flag_id` int unsigned NOT NULL AUTO_INCREMENT,
  `flag_val` varchar(30) NOT NULL,
  PRIMARY KEY (`flag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO flag(flag_id, flag_val) values(1, 'true');

Database Configuration

To establish database connection we need to have the following standard configuration in a file called application.properties under classpath folder src/main/resources.

#datasource
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/roytuts
spring.datasource.username=root
spring.datasource.password=root

Java configuration for database related beans:

package com.roytuts.spring.postconstruct.predestroy.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class Config {

	@Autowired
	private Environment environment;

	@Bean
	public DataSource dataSource() {
		return DataSourceBuilder.create().driverClassName(environment.getProperty("spring.datasource.driverClassName"))
				.url(environment.getProperty("spring.datasource.url"))
				.username(environment.getProperty("spring.datasource.username"))
				.password(environment.getProperty("spring.datasource.password")).build();
	}

	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}

}

POJO Class

The POJO class will map the table data with the Java class.

package com.roytuts.spring.postconstruct.predestroy.dto;

public class Flag {

	private Integer flagId;
	private String flagVal;

	//getters

}

Repository Class

Repository class is responsible for interacting with database for required activities:

package com.roytuts.spring.postconstruct.predestroy.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class FlagDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	private final String SQL_SELECT_FLAG_VAL = "select flag_val from flag where flag_id = 1";

	public String getFlag() {
		return jdbcTemplate.queryForObject(SQL_SELECT_FLAG_VAL, new Object[] {}, String.class);
	}

}

Service Class

Service class is responsible for performing necessary business processing.

package com.roytuts.spring.postconstruct.predestroy.service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.roytuts.spring.postconstruct.predestroy.dao.FlagDao;

@Service
public class FlagService {

	@Autowired
	private FlagDao flagDao;

	public void doFlagWork() {
		System.out.println("Do Flag Work");
	}

	@PostConstruct // do some initialization work
	public void selectFlag() {
		System.out.println("@PostConstruct");
		String val = flagDao.getFlag();
		setDbFlag(val);
	}

	@PreDestroy // do some destruction work (like releasing pooled connections)
	public void cleanFlag() {
		System.out.println("@PreDestroy");
		System.setProperty("dbFlag", "false");
		System.out.println("DB Flag: " + System.getProperty("dbFlag"));
	}

	private void setDbFlag(String dbFlag) {
		System.setProperty("dbFlag", dbFlag);
		System.out.println("DB Flag: " + System.getProperty("dbFlag"));
	}

}

Main Class

A class is having main method with @SpringBootApplication annotation will run the application.

package com.roytuts.spring.postconstruct.predestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.roytuts.spring.postconstruct.predestroy.service.FlagService;

@SpringBootApplication
public class SpringPostConstructPreDestroyApp implements CommandLineRunner {
	
	@Autowired
	private FlagService flagService;

	public static void main(String[] args) {
		SpringApplication.run(SpringPostConstructPreDestroyApp.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		flagService.doFlagWork();
		System.out.println("DB Flag in System Property: " + System.getProperty("dbFlag"));
	}
}

Testing the Application

Executing the above main class will give you the following output:

DB Flag: true
Do Flag Work
DB Flag in System Property: true
@PreDestroy
DB Flag: false

Look at the above output, we set the DB flag into System property after fetching from the database table once FlagService bean is created.

Also notice that just before the bean gets destroyed the DB flag is set to false.

Source Code

Download

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *