@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
- Introduction
- Key Concepts
- Using @PostConstruct and @PreDestroy in Spring
- Example Project Setup
- Real-World Use Case: Database Initialization
- Example Implementation
- Running the Application
- 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 typeMessageService
from the Spring context.- The
close
method oncontext
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 theDatabaseService
bean is initialized, setting up the database and inserting initial data. - The
init
method is called after theMessageServiceImpl
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 theMessageServiceImpl
bean is destroyed, cleaning up any resources. - The
cleanupDatabase
method is called before theDatabaseService
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
Post a Comment
Leave Comment