In the previous post Custom Spring Callback Methods I discussed about the customizing bean lifecycle callback methods which are required at the time of bean initialization and its destruction. In this post I shall cover another aspect of Spring Container Extension Points.
Note:Extension Points are well defined exposed places/hooks for others to provide extended functionality.
Typically, an application developer does not need to subclass ApplicationContext implementation classes. Instead, the Spring IoC container can be extended by plugging in implementations of special integration interfaces. The following few sections describe these integration interfaces.
BeanPostProcessor and BeanFactoryPostProcessor are the most commonly used extension points in Spring.
also read: follow us on @twitter and @facebook
- Spring Tutorials ( Collection for Spring reference documentations)
- Spring Framework Interview Questions
- Email Integration in Spring Framework
- Introduction to Spring REST Services
Customizing Spring beans using a BeanPostProcessor Interface
- The BeanPostProcessor interface defines callback methods that you can implement to provide your own (or override the container’s default) instantiation logic, dependency resolution logic, and so forth. If you want to implement some custom logic after the Spring container finishes instantiating, configuring, and otherwise initializing a bean, you can plug in one or more BeanPostProcessor implementations.
- So in essence the method postProcessBeforeInitialization defined in the BeanPostProcessor gets called (as the name indicates) before the initialization of beans (e.g. you want to load certain property file/read data from the remote source/service). Likewise the postProcessAfterInitialization gets called after the initialization of the bean.
- You can control the order in which these BeanPostProcessor interfaces execute by setting the order property. You can set this property only if the BeanPostProcessor implements the Ordered interface
- Classes that implement the BeanPostProcessor interface are special, and so they are treated differently by the container. All BeanPostProcessors and their directly referenced beans are instantiated on startup, as part of the special startup phase of the ApplicationContext
Example
The following example show how to write, register, and use BeanPostProcessors in an ApplicationContext.
- Create a project
- Create a project with a name CustomExtensionPoints and create a package com.javabeat under the src folder in the created project.
- Add Libraries
- Add required Spring libraries using Add External JARs option as explained in the previous article “Customizing callback methods”.
- Create source file
- Create Java classes HelloWorld, InstantiationTracingBeanPostProcessor and MainApp under the com.javabeat package.
Contents of HelloWorld.java are:
package com.javabeat; public class HelloWorld { private String message; public HelloWorld() { System.out.println(" ***** HelloWorld() constructor: HelloWorld Bean instantiated *****"); } public void setMessage(String message) { this.message = message; } public void getMessage() { System.out.println("The Greeting Message : " + message); } public void init() { System.out.println("Bean in init method-Initialization call back"); } public void destroy() { System.out.println("Bean in init method-Destruction call back"); } }
Contents of InstantiationTracingBeanPostProcessor.java are:
package com.javabeat; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { /* simply return the instantiated bean as-is*/ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeforeInitialization : " + beanName); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Bean '" + beanName + "' created : " + bean.toString()); System.out.println("AfterInitialization : " + beanName); return bean; } }
Contents of MainApp.java are:
package com.javabeat; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext( "Beans.xml"); HelloWorld obj = (HelloWorld) context.getBean("helloWorld"); obj.getMessage(); context.registerShutdownHook(); } }
4. Create Configuration file
Create Beans configuration file Beans.xml under the src folder. Contents of the Beans.xml are :
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloWorld" class="com.javabeat.HelloWorld" init-method="init" destroy-method="destroy"> <property name="message" value="Hello World!"/> </bean> <bean class="com.javabeat.InstantiationTracingBeanPostProcessor" /> </beans>
Notice that the name of the bean is not given, only the class attribute is defined.
Execute the code
The final step is run the application as explained below. Once all the above code is ready execute and the below output appears on the console:
***** HelloWorld() constructor: HelloWorld Bean instantiated ***** BeforeInitialization : helloWorld Bean in init method-Initialization call back Bean 'helloWorld' created : com.javabeat.HelloWorld@15ab10b AfterInitialization : helloWorld The Greeting Message : Hello World! Bean in init method-Destruction call back
The RequiredAnnotationBeanPostProcessor
Using callback interfaces or annotations in conjunction with a custom BeanPostProcessor implementation is a common means of extending the Spring IoC container. One of the best example is RequiredAnnotationBeanPostProcessor class.
In Spring we have an annotation called as @Required to make it mandatory that the dependency has to be injected. Just by using this annotation in the code will not work, since someone has to check whether the requirement has been met or not and report an error if not.That’s what this processor class does.
I shall give an example for this in the next article on “Annotation based container configuration”.
Customizing configuration metadata with a BeanFactoryPostProcessor
- The semantics of this interface are similar to the BeanPostProcessor, with one major difference: BeanFactoryPostProcessors affect BeanDefinition objects because they are run right after your configuration is read in. There are no bean instances created yet. So it can’t do anything to your objects instances yet.
Note:If you want to change the actual bean instances (the objects that are created from the configuration metadata), then use BeanPostProcessor. - One of the commonly used BeanFactoryPostProcessor is PropertyPlaceHolderConfigurer class. This class which looks through the BeanDefinition objects and replaces any ${property.name} with the actual value of property.name from a Properties file, or System.getProperty() or environment variables.
- The PropertyOverrideConfigurer, another bean factory post-processor, resembles the PropertyPlaceholderConfigurer, but unlike the latter, the original definitions can have default values or no values at all for bean properties. If an overriding Properties file does not have an entry for a certain bean property, the default context definition is used.
Customizing instantiation logic with a FactoryBean
Because of the complexity involved, we tend to write our own factory classes to initialize the complex beans. There are couple of ways to handle this situation in Spring framework::
- Traditional way of writing a factory class
- Using the FactoryBean API for writing a factory class
If the role of the factory class is precise and clear, for example, we might want to create a factory class which will return a customized DataSource for test environment then, option 2 is a good choice.
The FactoryBean interface provides three methods:
- Object getObject(): returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.
- boolean isSingleton(): returns true if this FactoryBean returns singletons, false otherwise.
- Class getObjectType(): returns the object type returned by the getObject() method or null if the type is not known in advance.
Summary
In this article we saw commonly used extension points in Spring, BeanPostProcessor and BeanFactoryPostProcessor. We saw a practical example for BeanPostProcessor. We also saw theory for customizing using BeanFactoryPostProcessor and FactoryBean. In the next article I shall discuss about “Annotation based container configuration”. If you are interested in receiving the future articles, please subscribe here. follow us on @twitter and @facebook