Bean Post Processor ( BPP ) and Custom Scope in Spring Framework
FBean Post Processor is used to interact with the newly created bean instance before or/and after their initialization method is invoded by spring container.
We can write our custom business logic before and after initialization of bean.It is applicable for the beans either we define as a programmatic/declarative or annotated.
We can do each spring bean object specific post processing by using custom init or initializing bean afterPropertySet or @PostConstruct method
If multiple spring beans and their objects are looking for common processing logic then instead of writing inside each and every spring bean
class, we can write only for one time outside of all spring beans by taking the support of Bean Post Processor ( BPP ) as the class that implements
org.springframework.beans.factory.config BeanPostProcessor interface
BeanPostProcessor interface contains 2 method
postProcessAfterInitialization(Object bean, String beanName)
This method will executes after spring bean init life cycle methods execution
Apply this BeanPostProcessor to the given new bean instance after any bean initialization callbacks (like InitializingBean’s afterPropertiesSet or a custom init-method).
PostProcessBeforeInitialization(Object bean, String beanName)
This method will executes before spring bean init life cycle methods execution
Apply this BeanPostProcessor to the given new bean instance before any bean initialization callbacks (like InitializingBean’s afterPropertiesSet or a custom init-method).
Take a simple example where 3 beans perform a single functionality. For each bean we require a created timestamp property and the scope of these beans are prototype. Now suppose there are 100 request comes, so there will be 300 bean instance executes and sets the same created timestamp property. unnecessary these initialization happens, instead of this we can write simple function in postProcessAfterInitialization method where we can transform bean with updated property with created time stamp. Below is the simple example where doing basic validation of beans.
Custom Scope
Spring provide a flexibility to extends scope, so we are not limited with the predefined scope available in spring, we can create our custom scope.
Scopes are defined by the org.springframework.beans.factory.config.Scope
interface. This is the interface that you will need to implement in order to integrate your own custom scope(s) into the Spring container
There are 4 methods available to create custom scope
Object get(String name, ObjectFactory objectFactory)
Object remove(String name)
void registerDestructionCallback(String name, Runnable destructionCallback)
String getConversationId()
CustomScopeConfigurer is the class available with scopes as map in instance variable. There are method like setScope accepting scopes with map so we can create a bean of CustomScopeConfigurer with map having custom scope say customScope and then implement the Scope interface. with customScope a bean is created and put in thread local varaible.I will continue in next story with detailed explanation. As of now you can refer below example.
public void setScopes(Map<String, Object> scopes) {this.scopes = scopes;}
App.java
package com.spring.beans.bpp;import java.util.HashMap;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.config.CustomScopeConfigurer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Scope;/**** @author Kablu**/@Configuration@ComponentScan(basePackages = “com.spring.beans.bpp”)public class App {@Scope(“customScope”)MyBean myBean() {MyBean m = new MyBean();m.setName(“Kablu”);return m;}@Bean(“customScopeConfigurer”)static CustomScopeConfigurer customScopeConfigurer() {CustomScopeConfigurer cs = new CustomScopeConfigurer();Map<String, Object> scopes = new HashMap<String, Object>();scopes.put(“customScope”, new MyThreadScope());cs.setScopes(scopes);return cs;}}
MyBean.java
package com.spring.beans.bpp;import javax.annotation.PostConstruct;import org.springframework.beans.factory.InitializingBean;/**** @author Kablu**/public class MyBean implements InitializingBean {String name;public MyBean() {System.out.println(“MyBean Constructor is called..”);}public String getName() {return name;}public void setName(String name) {this.name = name;}public void validateInstance() {System.out.println(“Validating bean”);this.name = “Nikhil”;}@PostConstructpublic void init() {System.out.println(“MyBean init method called”);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println(“afterPropertiesSet called for MyBean”);}}
MyBeanPostProcessor
package com.spring.beans.bpp;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component
public class MyBeanPostProcessor implements BeanPostProcessor {public MyBeanPostProcessor() {
System.out.println(“MyBeanPostProcessor Constructor Called..”);
}@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(“postProcessBeforeInitialization-Before Initialization method. Bean name is “ + beanName);
return bean;
}public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(“postProcessAfterInitialization — After Initialization method. Bean name is “ + beanName);
if (bean instanceof MyBean) {
((MyBean) bean).validateInstance();
}
return bean;
}
}
MyThreadScope
package com.spring.beans.bpp;import java.util.HashMap;import java.util.Map;import org.springframework.beans.factory.ObjectFactory;import org.springframework.beans.factory.config.Scope;/**** @author Kablu**/public class MyThreadScope implements Scope {private final ThreadLocal<Object> myThreadScope = new ThreadLocal<Object>() {protected Map<String, Object> initialValue() {System.out.println(“initialized ThreadLocal method”);return new HashMap<String, Object>();}};@Overridepublic Object get(String name, ObjectFactory<?> objectFactory) {@SuppressWarnings(“unchecked”)Map<String, Object> scope = (Map<String, Object>) myThreadScope.get();System.out.println(“getting object from scope.”);Object object = scope.get(name);if (object == null) {object = objectFactory.getObject();scope.put(name, object);}return object;}@Overridepublic String getConversationId() {return null;}@Overridepublic void registerDestructionCallback(String name, Runnable callback) {}@Overridepublic Object remove(String name) {System.out.println(“removing object from scope.”);@SuppressWarnings(“unchecked”)Map<String, Object> scope = (Map<String, Object>) myThreadScope.get();return scope.remove(name);}@Overridepublic Object resolveContextualObject(String name) {return null;}}
Main.java
package com.spring.beans.bpp;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/**** @author Kablu**/public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(App.class);MyBean bean2 = annotationConfigApplicationContext.getBean(MyBean.class);System.out.println(“Scope Bean Name:” + bean2.getName());MyBeanPostProcessor bean = annotationConfigApplicationContext.getBean(MyBeanPostProcessor.class);System.out.println(“BeanPostProcessor Bean Name:” + bean.postProcessAfterInitialization(bean2, “beanAnnot”));for (String scope : annotationConfigApplicationContext.getBeanFactory().getRegisteredScopeNames()) {System.out.println(“Registered Scope is:” + scope);}annotationConfigApplicationContext.close();}}
In the next story i will explain how and why custom scopes can be implement.Happy learning!!!!!