Spring @PostConstruct and @PreDestroy Example

In this article, we will discuss how to use the @PostConstruct and @PreDestroy annotations in a Spring Boot application. These annotations are part of the Java EE specification and are used to define lifecycle callback methods for a bean.

Table of Contents

  1. Introduction
  2. Key Concepts
  3. Using @PostConstruct and @PreDestroy in Spring
  4. Example Project Setup
  5. Real-World Use Case: Database Initialization
  6. Example Implementation
  7. Running the Application
  8. Conclusion

Introduction

The @PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. Similarly, the @PreDestroy annotation is used on methods that need to be executed before the bean is destroyed. These annotations are particularly useful for resource management and cleanup tasks.

Key Concepts

Using @PostConstruct and @PreDestroy in Spring

@PostConstruct

This annotation is used on a method that needs to be run after the bean has been initialized and all dependencies have been injected. It is typically used for performing any setup or initialization tasks that need to be done after the bean's properties have been set.

@PreDestroy

This annotation is used on a method that needs to be run before the bean is destroyed. It is typically used for cleanup tasks such as releasing resources or closing connections.

Example Project Setup

Let's create a simple Maven project to demonstrate the usage of @PostConstruct and @PreDestroy annotations in a Spring application.

1. Create a Simple Maven Project

Create a simple Maven project using your favourite IDE. Below is the project structure for your reference:


src
├── main
│   ├── java
│   │   └── net
│   │       └── javaguides
│   │           └── spring
│   │               ├── AppConfig.java
│   │               ├── Application.java
│   │               ├── MessageService.java
│   │               ├── MessageServiceImpl.java
│   │               └── DatabaseService.java
│   └── resources
│       └── application.properties
        

2. The pom.xml File

Make sure to use Java 17 or later for Spring Framework 6:


<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
    <groupId>net.javaguides
    <artifactId>spring-postconstruct-predestroy
    <version>1.0-SNAPSHOT
    <properties>
        <maven.compiler.source>17
        <maven.compiler.target>17
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot
            <artifactId>spring-boot-starter
            <version>3.0.0
        </dependency>
        <dependency>
            <groupId>org.springframework.boot
            <artifactId>spring-boot-starter-data-jpa
            <version>3.0.0
        </dependency>
        <dependency>
            <groupId>com.h2database
            <artifactId>h2
            <scope>runtime
        </dependency>
    </dependencies>
</project>
        

Real-World Use Case: Database Initialization

In a real-world application, you might need to initialize a database at startup and clean up resources when the application shuts down. The @PostConstruct and @PreDestroy annotations are perfect for such tasks.

Example Implementation

3. Create the MessageService Interface

MessageService.java


package net.javaguides.spring;

public interface MessageService {
    String getMessage();
}
            

4. Create the MessageServiceImpl Class

MessageServiceImpl.java


package net.javaguides.spring;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class MessageServiceImpl implements MessageService {

    private String message;

    @PostConstruct
    public void init() {
        message = "Hello, Spring!";
        System.out.println("PostConstruct: MessageService initialized with message: " + message);
    }

    @Override
    public String getMessage() {
        return message;
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("PreDestroy: Cleaning up resources...");
    }
}
            

5. Create the DatabaseService Class

DatabaseService.java


package net.javaguides.spring;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;

@Component
public class DatabaseService {

    private final DataSource dataSource;

    public DatabaseService(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @PostConstruct
    public void initDatabase() {
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {
            statement.execute("CREATE TABLE IF NOT EXISTS Users (id INT AUTO_INCREMENT, name VARCHAR(255), PRIMARY KEY (id))");
            statement.execute("INSERT INTO Users (name) VALUES ('John Doe')");
            System.out.println("PostConstruct: Database initialized");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @PreDestroy
    public void cleanupDatabase() {
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {
            statement.execute("DROP TABLE IF EXISTS Users");
            System.out.println("PreDestroy: Database cleaned up");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
            

6. Create the AppConfig Class

AppConfig.java


package net.javaguides.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "net.javaguides.spring")
public class AppConfig {
}
            

7. Create the Main Application Class

Application.java


package net.javaguides.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MessageService messageService = context.getBean(MessageService.class);
        System.out.println("Message from MessageService: " + messageService.getMessage());
        context.close();
    }
}
            

In this class:

Running the Application

When you run the Application class, you should see the following output:


PostConstruct: Database initialized
PostConstruct: MessageService initialized with message: Hello, Spring!
Message from MessageService: Hello, Spring!
PreDestroy: Cleaning up resources...
PreDestroy: Database cleaned up
        

Explanation:

Conclusion

In this article, we demonstrated how to use the @PostConstruct and @PreDestroy annotations in a Spring Boot application. By using these annotations, we can perform initialization and cleanup tasks for our beans. The example showed how to initialize a database at startup and clean up resources when the application shuts down, which is a common real-world use case.