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

  • @PostConstruct: Annotates a method that should be executed after the bean's dependencies have been injected.
  • @PreDestroy: Annotates a method that should be executed just before the bean is removed from the context or destroyed.

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

  • AnnotationConfigApplicationContext is used to create and manage the Spring application context.
  • context.getBean(MessageService.class) retrieves a bean of type MessageService from the Spring context.
  • The close method on context triggers the @PreDestroy method.

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:

  • The initDatabase method is called after the DatabaseService bean is initialized, setting up the database and inserting initial data.
  • The init method is called after the MessageServiceImpl bean is initialized, setting the message and printing the initialization message.
  • The getMessage method prints the message to the console.
  • The cleanup method is called before the MessageServiceImpl bean is destroyed, cleaning up any resources.
  • The cleanupDatabase method is called before the DatabaseService bean is destroyed, dropping the database table.

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.

Comments