During the Spring Application Development, sometimes when the spring beans are created developers are required to execute the initialization operations and the cleanup operations before the bean is destroyed. In the spring framework, we can use the init-method and the destroy-method labels in the bean configuration.
init() Method
In the real-world application init() method is the one you will find it everywhere. init-method is the attribute of the spring <bean> tag. It is utilized to declare a custom method for the bean that will act as the bean initialization method. We can define it as follows.
<bean id="student" class="com.amiya.Student" init-method="myPostConstruct">
Here myPostConstruct() method is the bean initialization method in the Student class. This initialization method is called after initializing bean properties. We can use such an initialization method to validate the value of bean properties or initialize any task. The InitializingBean interface in spring performs the same task but it is highly coupled to spring, so we should prefer the init-method.
Why init()?
- You can add custom code/logic during bean initialization
- It can be used for setting up resources like database/socket/file etc.
destroy() Method
The destroy() method will be called before the bean is removed from the container. destroy-method is bean attribute using which we can assign a custom bean method that will be called just before the bean is destroyed. To use the destroy-method attribute, we do as follows.
<bean id="student" class="com.amiya.Student" destroy-method="myPreDestroy">
Here myPreDestroy() method will be defined in the Student class. Spring will call this method just before destroying the bean. destroy-method is used to release resources or perform some destruction task. DisposableBean interface in spring performs the same task but it is highly coupled to spring, so we should prefer destroy-method. So let’s understand these two methods with a simple example.
Tip: It is good to go with JDBC Tutorial prior because it serves as a pre-requisite.
Implementation:
We are going to explain init() and destroy() Methods through @PostConstruct and @PreDestroy Annotation by simply creating a Spring JDBC project. So let’s create a Spring JDBC project first.
Step 1: Create a simple Java project in your preferred IDE (IntelliJ IDEA or Eclipse).
Tip: You can refer to below listed set of articles as follows:
Step 2: Create some tables inside your database. In this article, we have used the MySQL database. And the following data has been present inside our MySQL Database
So here studentdb is our schema name and hostelstudentinfo is the table name. Similarly, you can create your own schema and table and put some data inside that table manually.
Tip: You can refer to below listed set of articles as follows:
Step 3: Go to the Java project and create one class named StudentDAO and inside the class, we are going to create a single method selectAllRows() to fetch all the data present inside our MySQL database. We are also going to declare our four most important attributes to connect our Java program with the MySQL server listed below as follows:
- Driver
- URL
- User
- Password
Example
Java
// Java Program to Illustrate StudentDAO Class // Importing required classes import java.sql.*; // Class public class StudentDAO { // Class data members private String driver; private String url; private String userName; private String password; // Setter methods for // Setter Injection public void setDriver(String driver) { // This keyword refers to current instance itself this .driver = driver; } // Setter public void setUrl(String url) { this .url = url; } // Setter public void setUserName(String userName) { this .userName = userName; } // Setter public void setPassword(String password) { this .password = password; } // Method // To select all rows public void selectAllRows() throws ClassNotFoundException, SQLException { System.out.println( "Retrieving all student data.." ); // Loading drivers // using forName() method Class.forName(driver); // Getting a connection Connection con = DriverManager.getConnection( url, userName, password); // Executing a query Statement stmt = con.createStatement(); // Storing it in a ResultSet ResultSet rs = stmt.executeQuery( "SELECT * FROM studentdb.hostelstudentinfo" ); while (rs.next()) { int studentId = rs.getInt( 1 ); String studentName = rs.getString( 2 ); double hostelFees = rs.getDouble( 3 ); String foodType = rs.getString( 4 ); System.out.println(studentId + " " + studentName + " " + hostelFees + " " + foodType); } // Closing the connection // using close() method con.close(); } } |
Step 4: Now we have to Add the External JAR Files to an IntelliJ IDEA Project. A JAR (Java Archive) is a package file format typically used to aggregate many Java class files and associated metadata and resources (text, images, etc.) into one file to distribute application software or libraries on the Java platform. In simple words, a JAR file is a file that contains a compressed version of .class files, audio files, image files, or directories. We have to add the following external jar files to our Java project
- Spring
- MySQL Connector
Tip: You can refer to the listed article: How to Add External JAR File to an IntelliJ IDEA Project for which user may download the jar file from the links provided below as follows:
Step 5: Let’s create the bean of StudentDAO class inside the beans.xml file and inject the values of the properties by setter injection.
Tip: You can refer to the listed article: Spring – Injecting Literal Values By Setter Injection.
File: beans.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/beans < bean id = "studentDAO" class = "StudentDAO" > < property name = "driver" value = "com.mysql.cj.jdbc.Driver" /> < property name = "userName" value = "root" /> < property name = "password" value = "your password" /> </ bean > </ beans > |
Step 6: Create the Main class and let’s test our application is running fine or not.
File: Main.java
Java
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.sql.SQLException; public class Main { public static void main(String[] args) throws SQLException, ClassNotFoundException { // Using ApplicationContext tom implement Spring IoC ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" ); // Get the bean studentDAO StudentDAO studentDAO = context.getBean( "studentDAO" , StudentDAO. class ); // Calling the method studentDAO.selectAllRows(); } } |
Output:
One can see we have successfully fetched the data from the MySQL Database.
Retrieving all student data.. 1 Asish 300.5 Veg 2 Vicky 245.89 Non Veg 3 Anshul 123.67 Veg
So what’s the problem with the above code? The problem is, suppose we want to define another method deleteStudentRecord(int studentId) in the StudentDAO class something like as represented in below code chunk as follows:
File: StudentDAO Class
public void deleteStudentRecord(int studentId) throws ClassNotFoundException, SQLException { // Display command only System.out.println("Deleting student data.."); // Loading driver // using forname() method Class.forName(driver); // Getting a connection Connection con = DriverManager.getConnection(url, userName, password); // Executing a query Statement stmt = con.createStatement(); stmt.executeUpdate("delete from studentdb.hostelstudentinfo where Student_id = " + studentId); // Printing the deleted record to corresponding ID System.out.println("Record Deleted with the ID " + studentId); // Close the connection // using close() method con.close(); }
Now you can see we have to write some lines of code again and again. Then think if suppose we want to perform 100 different operations we have to write the same code again and again. So what we can do. We can write the common code inside a method and we can invoke the method. We can write something like this inside a method named createStudentDBConnection()
public void createStudentDBConnection() throws ClassNotFoundException, SQLException { // Loading driver // using forName() method Class.forName(driver); // Getting connection // using DriverManager con = DriverManager.getConnection(url, userName, password); // Executing query stmt = con.createStatement(); }
Similarly, we can also define another method to close the connection. We can write something like this inside a method named closeConnection()
public void closeConnection() throws SQLException { con.close(); }
And we can invoke those methods inside the selectAllRows() and deleteStudentRecord() something like this. Now the complete code for the StudentDAO.java class can be written as
File: StudentDAO.java
Java
// Java Program to Illustrate StudentDAO Class // Importing required classes import java.sql.*; // Class public class StudentDAO { // Class data members private String driver; private String url; private String userName; private String password; // Connection Object Connection con; // Statement Object Statement stmt; // Setter public void setDriver(String driver) { this .driver = driver; } // Setter public void setUrl(String url) { this .url = url; } // Setter public void setUserName(String userName) { this .userName = userName; } // Setter public void setPassword(String password) { this .password = password; } // Method // Create connection public void createStudentDBConnection() throws ClassNotFoundException, SQLException { // Loading driver using forName() method Class.forName(driver); // Getting a connection con = DriverManager.getConnection(url, userName, password); // Executing a query stmt = con.createStatement(); } // Method // To select all rows public void selectAllRows() throws ClassNotFoundException, SQLException { // Display message System.out.println( "Retrieving all student data.." ); // Create connection createStudentDBConnection(); // Storing it in a ResultSet ResultSet rs = stmt.executeQuery( "SELECT * FROM studentdb.hostelstudentinfo" ); while (rs.next()) { int studentId = rs.getInt( 1 ); String studentName = rs.getString( 2 ); double hostelFees = rs.getDouble( 3 ); String foodType = rs.getString( 4 ); // Printing student attributes System.out.println(studentId + " " + studentName + " " + hostelFees + " " + foodType); } // Closing connection closeConnection() } // Method public void deleteStudentRecord( int studentId) throws ClassNotFoundException, SQLException { System.out.println( "Deleting student data.." ); // Create connection createStudentDBConnection(); stmt.executeUpdate( "delete from studentdb.hostelstudentinfo where Student_id = " + studentId); System.out.println( "Record Deleted with the ID " + studentId); // Close the connection closeConnection(); } // Method // Close the connection public void closeConnection() throws SQLException { con.close(); } } |
But we are doing the same thing again. We are calling the createStudentDBConnection() and closeConnection() method again and again inside the selectAllRows() and deleteStudentRecord() methods. Suppose we have a total of 100 utility methods like that then we have to call these methods 100 times. So we will tell our spring framework that once you (spring framework) create the bean please execute the createStudentDBConnection() method. So the way we are going to tell the spring framework is by putting the @PostConstruct annotation. In summary, we can tell that hey Spring, once you create the StudentDAO object please create createStudentDBConnection() by yourself. Don’t wait for us to call it.
Spring @PostConstruct Annotation: So generally, whenever we annotate a method in Spring Bean with @PostConstruct annotation, it gets executed after the spring bean is initialized. We can have only one method annotated with @PostConstruct annotation. This annotation is part of Common Annotations API and it’s part of JDK module javax.annotation-api.
Note: Adding @PostConstruct and @PreDestroy Annotation in the Project
Developers, who are using Java 9+, both @PostConstruct and @PreDestroy annotations are part of Java EE. And since Java EE has been deprecated in Java 9 and removed in Java 11 we have to add an additional dependency to use these annotations:
If one is using Maven, then
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
Else, if you add the jar manually, it is as follows:
- Visit the link and inside the files column, one can see pom and jar.
- Click on the jar and your jar will be downloaded.
- Add it to the classpath as an external jar by configuring the build.
One can refer to this article: Add External JAR File to an IntelliJ IDEA Project or refer article: Add External JAR File to an Eclipse Project.
Note: If you are on Java 8 or the lower version, then you won’t have to perform the above stuff.
So now we can put the @PostConstruct Annotation before the createStudentDBConnection() something like this
@PostConstruct public void createStudentDBConnection() throws ClassNotFoundException, SQLException { // Loading driver Class.forName(driver); // Getting a connection con = DriverManager.getConnection(url, userName, password); // Executing query stmt = con.createStatement(); }
Introducing init()
In the real-world application init() method is the one you will find it everywhere. We can also say the init() method is a standard name. For example, in this project, we can mark the createStudentDBConnection() method as init. Here the createStudentDBConnection() is the init() method for us. Annotate a method with @PostConstruct to use it as a init() method. We don’t need to call the init() method, our framework will call it for us. We can give our init() method name as anything. We may say it init() or we may say it createStudentDBConnection() or xyz(). So now we can write the same thing like that
@PostConstruct public void init() throws SQLException, ClassNotFoundException { createStudentDBConnection(); } // Method public void createStudentDBConnection() throws ClassNotFoundException, SQLException { // Loading driver Class.forName(driver); // Getting a connection con = DriverManager.getConnection(url, userName, password); // Executing query stmt = con.createStatement(); }
Why init()?
- You can add custom code/logic during bean initialization
- It can be used for setting up resources like database/socket/file etc.
Introducing destroy()
The destroy() method will be called before the bean is removed from the container. Now let’s coming to the closeConnection() method. We defined this method for cleaning up unused references. For example in this project the con variable. So we can make any method as destroy() method by marking it as @PreDestroy. So now we can write the code fragment will look like as follows:
// Method public void closeConnection() throws SQLException { con.close(); } // Annotation @PreDestroy // Method public void destroy() throws SQLException { closeConnection(); }
Spring @PreDestroy Annotation: When we annotate a Spring Bean method with PreDestroy annotation, it gets called when the bean instance is getting removed from the context. Remember that if your spring bean scope is “prototype” then it’s not completely managed by the spring container and the PreDestroy method won’t get called. If there is a method named shutdown or close then the spring container will try to automatically configure them as callback methods when the bean is being destroyed.
Note that we can also use the @PreDestroy annotation before the closeConnection() method.
File: StudentDAO.java
Java
// Java Program to Illustrate StudentDAO Class // Importing required classes import java.sql.*; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; // Class public class StudentDAO { // Class data members private String driver; private String url; private String userName; private String password; // Connection Object Connection con; // Statement Object Statement stmt; // Setter public void setDriver(String driver) { this .driver = driver; } // Setter public void setUrl(String url) { this .url = url; } // Setter public void setUserName(String userName) { this .userName = userName; } // Setter public void setPassword(String password) { this .password = password; } @PostConstruct public void init() throws SQLException, ClassNotFoundException { createStudentDBConnection(); } // Method 1 public void createStudentDBConnection() throws ClassNotFoundException, SQLException { // Loading driver // using forName() method Class.forName(driver); // Getting a connection // using DriverManager con = DriverManager.getConnection(url, userName, password); // Executing query stmt = con.createStatement(); } // Method 2 public void selectAllRows() throws ClassNotFoundException, SQLException { // Display command System.out.println( "Retrieving all student data.." ); // Storing in a ResultSet ResultSet rs = stmt.executeQuery( "SELECT * FROM studentdb.hostelstudentinfo" ); // Iterating while (rs.next()) { int studentId = rs.getInt( 1 ); String studentName = rs.getString( 2 ); double hostelFees = rs.getDouble( 3 ); String foodType = rs.getString( 4 ); // Printing corresponding student attributes System.out.println(studentId + " " + studentName + " " + hostelFees + " " + foodType); } } // Method 3 public void deleteStudentRecord( int studentId) throws ClassNotFoundException, SQLException { System.out.println( "Delete student data.." ); stmt.executeUpdate( "delete from studentdb.hostelstudentinfo where Student_id = " + studentId); System.out.println( "Record Deleted with the ID " + studentId); } // Method 4 public void closeConnection() throws SQLException { con.close(); } // Method 5 @PreDestroy public void destroy() throws SQLException { closeConnection(); } } |
Before running the application make sure you have added the following line inside your beans.xml file to use the annotation
<context:annotation-config/>
File: beans.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/beans < context:annotation-config /> < bean id = "studentDAO" class = "StudentDAO" > < property name = "driver" value = "com.mysql.cj.jdbc.Driver" /> < property name = "userName" value = "root" /> < property name = "password" value = "143@Arpilu" /> </ bean > </ beans > |
File: Main.java
Java
// Java Program to Illustrate Application Class // Importing required classes import java.sql.SQLException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; // Application Class public class Main { // Main driver method public static void main(String[] args) throws SQLException, ClassNotFoundException { // Using ApplicationContext tom implement Spring IoC ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" ); // Getting the bean studentDAO StudentDAO studentDAO = context.getBean( "studentDAO" , StudentDAO. class ); // Calling the method // inside main() method studentDAO.deleteStudentRecord( 2 ); studentDAO.selectAllRows(); } } |
Output:
Delete student data.. Record Deleted with the ID 2 Retrieving all student data.. 1 Asish 300.5 Veg 3 Anshul 123.67 Veg