BeanPostProcessor Example | Spring Boot

BeanPostProcessor is an interface included with the Spring framework.

BeanPostProcessor defines two methods...postProcessBeforeInitialization() and postProcessAfterInitialization()...

BeanPostProcessor | Basic Example

MyCustomBeanPostProcessor.java

package com.example.demo;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyCustomBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("PostProcessor Before Initialization called '" + beanName + bean.toString());
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("PostProcessor After Initialization called" + beanName + bean.toString());
        return bean;
    }
}

Person.java

package com.example.demo;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class Person {
    public Person(){
        System.out.println("Constructor called for Person");
    }

    @PostConstruct
    void init() {
        System.out.println("Init method called for Person");
    }
}

DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}

The application logs the statements in the following order...

Constructor called for Person
PostProcessor Before Initialization called 'personcom.example.demo.Person@547e29a4
Init method called for Person
PostProcessor After Initialization calledpersoncom.example.demo.Person@547e29a4

Notice how MyCustomBeanPostProcessor implements the BeanPostProcessor interface.

The @PostConstruct annotation tells the Person class to execute init() after postProcessBeforeInitialization() but before postProcessAfterInitialization()...

This gives you more fine grain control over how beans are initialized and managed by the application context.

By implementing the BeanPostProcessor interface, you create a custom modification of the new Spring beans created both before and after they are initialized.

Why implement BeanPostProcessor?

Our basic example simply logs events based on when the methods are executed. This is not very useful in real world programming.

The main advantage of implementing BeanPostProcessor is that it gives us the ability to alter/modify the configuration of managed Spring beans throughout the application lifecycle.

Hooking into the lifecycle of a bean is especially useful when wrapping beans in proxies...

BeanPostProcessor | Proxy Example

Place.java

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class Place {

    @Autowired
    Person person;
    public Place(){
    }

    @PostConstruct
    public void init(){
        System.out.println("Place init() method called");
        person.sayHello();
    }
}

Person.java

package com.example.demo;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class Person {
    public Person(){
        System.out.println("Person constructor method called");
    }

    public void sayHello(){
        System.out.println("Hello!");
    }

    @PostConstruct
    void init() {
        System.out.println("Person init() method called");
    }
}

MyCustomBeanPostProcessor.java

@Component
public class MyCustomBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof Person) {
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvice(new LoggingInterceptor());
            return proxyFactory.getProxy();
        }
        return bean;
    }

    private static class LoggingInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            System.out.println("Before sayHello() method called");
            Object returnValue = methodInvocation.proceed();
            System.out.println("After sayHello() method called");
            return returnValue;
        }
    }
}

The application logs the following output...

Person constructor method called
Person init() method called
Place init() method called
Before sayHello() method called
Hello!
After sayHello() method called

For each bean, the MyCustomBeanPostProcessor creates a proxy class via the Spring AOP ProxyFactory() IF the bean is an instance of Person.

By creating a proxy, the BeanPostProcessor hook methods can be used to execute logic before/after method calls via the LoggingInterceptor class.

Proxies are a more practical application of the BeanPostProcessor interface. This is how many AOP features work with Spring including @Autowiring etc.

BeanPostProcessor vs PostConstruct

Both examples use the @PostConstruct annotation

Remember that BeanPostProcessor is an interface whereas @PostConstruct is an annotation.

Methods annotated with @PostConstruct execute after the constructor function is called.

Methods annotated with @PostConstruct execute AFTER the BeanPostProcessor postProcessBeforeInitialization() method.

Methods annotated with @PostConstruct execute BEFORE the BeanPostProcessor postProcessAfterInitialization() method.

@PostConstruct is a javax library annotation whereas BeanPostProcessor is a Spring framework interface.

How does BeanPostProcessor work?

Classes that implement the BeanPostProcessor interface are special types of Beans created before other beans are initialized.

This gives these beans the ability to hook into the initialization (pre and post) of classes managed by the application context.

The main advantage with this is found in creating proxies for beans where additional behavior / logic can be executed or wrapped around the "target" bean.

Processors that populate beans based on special marker interfaces implement the postProcessBeforeInitilization() method.

Processors that wrap beans with proxies (like the example above) implement the postProcessAfterInitialization() method.

BeanFactoryPostProcessor vs BeanPostProcessor

BeanFactoryPostProcessor and BeanPostProcessor are both Spring interfaces for custom modification of a Spring instance.

BeanFactoryPostProcessor is implemented for customizing the application context, including the bean definitions themselves.

BeanPostProcessor is implemented for customizing beans themselves during their lifecycle within the application.

You would use BeanFactoryPostProcessor if you want to customize or override properties configured in the application context.

You would use BeanPostProcessor to add custom functionality around the normal life cycle of a bean.

BeanPostProcessor not getting called?

If a class that implements BeanPostProcessor is not getting called then make sure the class is being registered with the application context...

If you are using Java based configuration then don't forget to include an annotation like @Component so the class is recognized by Spring...

@Component
public class MyCustomBeanPostProcessor implements BeanPostProcessor {
...

Alternatively, you can use xml based configuration to register with the application context.

Your thoughts?

|

wow this was a good read. it has all of the topics we care about when we are researching BeanPostProcessor.