Spring Framework provides BeanPostProcessor Interface. It allows custom modification of new bean instances that are created by Spring Bean Factory. If we want to implement some custom logic such as checking for marker interfaces or wrapping beans with proxies after the Spring container finishes instantiating, configuring, and initializing a bean, we can plug in BeanPostProcessor implementations.
Syntax:
org.springframework.beans.factory.config public interface BeanPostProcessor
Methods in BeanPostProcessor Interface
BeanPostProcessor interface consists of two callback methods namely listed as follows:
- postProcessBeforeInitialization() Method
- postProcessAfterInitialization() Method
Method 1: postProcessBeforeInitialization():
To apply any custom logic to the given new bean instance before any bean initialization callbacks (like InitializingBean’s afterPropertiesSet or a custom init-method), we can call this BeanPostProcessor method. The bean will already be populated with the property values and the returned bean instance may be a wrapper around the original one.
Syntax:
@Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
Parameters:
- The new bean instance
- The name of the bean
Return Type: Either the original or a wrapped bean instance to use. No subsequent BeanPostProcessors will be invoked, if null. And in case of errors, it throws BeansException.
Method 2: postProcessAfterInitialization():
To apply any custom logic to the given new bean instance after any bean initialization callbacks (like InitializingBean’s afterPropertiesSet or a custom init-method), we can call this BeanPostProcessor method.
The bean will already be populated with the property values and the returned bean instance may be a wrapper around the original one. This callback will be invoked for both the FactoryBean instance and the objects created by the FactoryBean. The post-processor can decide whether to apply to either the FactoryBean or created objects or both through the corresponding bean instance of the FactoryBean checks.
Syntax:
@Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
Parameters:
- The new bean instance
- The name of the bean
Return Type: Either the original or a wrapped bean instance to use. No subsequent BeanPostProcessors will be invoked, if null. And in case of errors, it throws BeansException.
Registering and Ordering the BeanPostProcessors
We can plug in one or more BeanPostProcessor implementations to include custom logic, in this case, we can control these multiple BeanPostProcessor instances by setting the order property or by implementing the Ordered interface.
An ApplicationContext can autodetect BeanPostProcessor beans in its bean definitions and apply those to any beans that are subsequently created. In case of a plain BeanFactory, we need to register the post-processors programmatically applying them to all beans created through the bean factory.
BeanPostProcessor beans that are autodetected in an ApplicationContext will be ordered according to PriorityOrdered and Ordered interfaces. In contrast, BeanPostProcessor beans that are registered programmatically with a BeanFactory will be applied in the order of registration.
Implementation:
We will create a simple Spring application to get employee details using Eclipse IDE.
Step 1: Create a new Java project ‘Spring_Application’ in Eclipse.
Step 2: Add Spring jar files to the project. We can download the latest jar files from Maven Repository.
Step 3: Now, we need to create the required Java classes and the configuration files under the project. Below will be the final project structure of the application.
Step 4: Create a ‘CustomProcessor.java’ file that implements the BeanPostProcessor interface and implements the two callback methods in it.
Example: Spring BeanPostProcessor
A. File: CustomProcessor.java
Java
// Java Program to Illustrate CustomProcessor Class package com.geeks.beans; // Importing required classes import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; // Class // Implementing BeanPostProcessor interface public class CustomProcessor implements BeanPostProcessor { // Method 1 public Object postProcessBeforeInitialization( Object bean, String beanName) throws BeansException { System.out.println( "postProcessBeforeInitialization() is called for EmployeeImpl" ); return bean; } // Method 2 public Object postProcessAfterInitialization( Object bean, String beanName) throws BeansException { System.out.println( "postProcessAfterInitialization() is called for EmployeeImpl" ); return bean; } } |
Step 5: Create an ‘Employee.java’ file to define variables and their getter/setter methods.
B. File: Employee.java
Java
// Java Program to Illustrate Employee Class package com.geeks.beans; // Class public class Employee { // Class data members private String name; private String mail; // Getter public String getName() { return name; } // Setter public void setName(String name) { this .name = name; } // Getter public String getMail() { return mail; } // Setter public void setMail(String mail) { this .mail = mail; } } |
Step 6: Create an ‘EmployeeImpl.java’ file to create a new Employee object. Define Init and Destroy methods of the class.
C. File: EmployeeImpl.java
Java
// Java Program Implementing Employee Class package com.geeks.beans; // Class public class EmployeeImpl { // Method 1 public Employee createEmp() { // Creating Employee class object // inside EmployeeImpl class Employee e = new Employee(); // Custom setters e.setName( "Geek" ); e.setMail( "test@email.com" ); return e; } // Method 2 public void initBean() { System.out.println( "EmployeeImpl is Initialized." ); } // Method 3 public void destroyBean() { System.out.println( "EmployeeImpl is Destroyed." ); } } |
Step 7: Create ‘applicationContext.xml’ Spring configuration file to include bean definitions. Register the BeanPostProcessor in the configuration file.
D. File: applicationContext.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/beans < bean id = "beanPostProcessor" class = "com.geeks.beans.CustomProcessor" /> < bean id = "impl" class = "com.geeks.beans.EmployeeImpl" init-method = "initBean" destroy-method = "destroyBean" /> </ beans > |
Step 8: Create an ‘EmployeeTest.java’ file to get the bean and run the application.
E. File: EmployeeTest.java
Java
// Java Program to Illustrate EmployeeTest Class package com.geeks.beans; // Importing required classes import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; // Class public class EmployeeTest { // Main driver method public static void main(String[] args) { // Creating object of ApplicationContext, // EmployeeImpl, and Employee class inside main() // method ApplicationContext con = new ClassPathXmlApplicationContext( "com/geeks/resources/applicationContext.xml" ); EmployeeImpl impl = (EmployeeImpl)con.getBean( "impl" ); Employee emp = impl.createEmp(); System.out.println( "Employee Details" ); System.out.println( "Name: " + emp.getName() + ", Email address: " + emp.getMail()); ((ClassPathXmlApplicationContext)con).close(); } } |
Step 9: Run the application as a Java application. We will get the below output in the console.
As we can see in the output, postProcessBeforeInitialization() is called before the initialization of the bean and once it is done, postProcessAfterInitialization() method is called and then the rest of the process completed. So, we can include any custom logic using post-processors to the new bean instances either before or after the bean initializations.