Ashish Sarin is a Sun Certified Enterprise Architect with more than 13 years of experience in designing and developing Java EE applications. He is the author of Getting started with Spring Framework (self-published), Spring Roo 1.1 Cookbook (by Packt Publishing) and Portlets in Action (by Manning Publications). Ashish has posted 4 posts at DZone. You can read more from them at their website. View Full User Profile

Interacting with newly created bean instances using Spring's BeanPostProcessor

12.12.2012
| 7267 views |
  • submit to reddit

This article is taken from the book Getting started with Spring Framework

 

BeanPostProcessor is used to interact with newly created bean instances before and/or after their initialization method is invoked by the Spring container. You can use BeanPostProcessor to execute custom logic before and/or after bean’s initialization method is invoked by the Spring container.

BeanPostProcessor interface defines the following methods:

  • Object postProcessBeforeInitialization(Object bean, String beanName) – this method is invoked before the initialization method of a bean instance is invoked
  • Object postProcessAfterInitialization(Object bean, String beanName) – this method is invoked after the initialization method of a bean instance is invoked

BeanPostProcessor’s methods accept newly created bean instance and its name as arguments, and return the same or modified bean instance. You configure a BeanPostProcessor implementation in the application context XML file like any other Spring bean. Once the BeanPostProcessor beans are created, the Spring container invokes each BeanPostProcessor’s postProcessBeforeInitialization and postProcessAfterInitialization methods for each bean instance created by the Spring container.

BeanPostProcessor example – Validating bean instances

In a Spring application, you may want to verify that a bean instance is configured correctly before it is injected into dependent beans or accessed by other objects in the application. Let’s see how we can use a BeanPostProcessor implementation to give an opportunity to each bean instance to validate its configuration before the bean instance is made available to dependent beans or other application objects.

The following example listing shows an InstanceValidator interface that must be implemented by beans whose configuration we want to validate using a BeanPostProcessor implementation:

Example listing – InstanceValidator interface

package sample.spring.chapter04.springbankapp.common;

public interface InstanceValidator {
  void validateInstance();
}

InstanceValidator interface defines a validateInstance method that verifies whether the bean instance was correctly initialized or not. We’ll soon see that the validateInstance method is invoked by a BeanPostProcessor implementation.

The following example listing shows the FixedDepositDaoImpl class that implements InstanceValidator interface:

Example listing – FixedDepositDaoImpl class

package sample.spring.chapter04.springbankapp.dao;

import org.apache.log4j.Logger;
import sample.spring.chapter04.springbankapp.common.InstanceValidator;

public class FixedDepositDaoImpl implements FixedDepositDao, InstanceValidator {

  private static Logger logger = Logger.getLogger(FixedDepositDaoImpl.class);

  private DatabaseConnection connection;

  public FixedDepositDaoImpl() {
    logger.info("FixedDepositDaoImpl's constructor invoked");
  }

  public void initializeDbConnection() {

  logger.info("FixedDepositDaoImpl's initializeDbConnection method invoked");
   connection = DatabaseConnection.getInstance();
  }

  @Override
  public void validateInstance() {
    logger.info("Validating FixedDepositDaoImpl instance");
     if(connection == null) {
     logger.error("Failed to obtain DatabaseConnection instance");
   }
  }
}

In the above example listing, the initializeDbConnection method is the initialization method that retrieves an instance of DatabaseConnection by calling getInstance static method of DatabaseConnection class. The connection attribute is null if FixedDepositDaoImpl instance fails to retrieve an instance of DatabaseConnection. If connection attribute is null, the validateInstance method logs an error message indicating that the FixedDepositDaoImpl instance is not correctly initialized. As the initializeDbConnection initialization method sets the value of connection attribute, the validateInstance method must be invoked after the initializeDbConnection method. In a real world application development scenario, if a bean instance is not configured correctly, the validateInstance method may take some corrective action or throw a runtime exception to stop the application from starting up. For simplicity, the validateInstance method logs an error message if a bean instance is not configured correctly.

The following example listing shows the InstanceValidationBeanPostProcessor class that implements Spring’s BeanPostProcessor interface, and is responsible for invoking validateInstance method of newly created beans:

Example listing – InstanceValidationBeanPostProcessor class

package sample.spring.chapter04.springbankapp.postprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;

public class InstanceValidationBeanPostProcessor implements BeanPostProcessor, Ordered {
    private static Logger logger = Logger.getLogger(InstanceValidationBeanPostProcessor.class);
    private int order;

    public InstanceValidationBeanPostProcessor() {
        logger.info("Created InstanceValidationBeanPostProcessor instance");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        logger.info("postProcessBeforeInitialization method invoked");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        logger.info("postProcessAfterInitialization method invoked");
        if (bean instanceof InstanceValidator) {
            ((InstanceValidator) bean).validateInstance();
        }
        return bean;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return order;
    }
}

The above example listing shows that the InstanceValidationBeanPostProcessor class implements Spring’s BeanPostProcessor and Ordered interfaces. The postProcessBeforeInitialization method simply returns the bean instance passed to the method. In the postProcessAfterInitialization method, if the bean instance is found to be of type InstanceValidator, the bean instance’s validateInstance method is invoked. This means that if a bean implements InstanceValidator interface, InstanceValidationBeanPostProcessor calls validateInstance method of the bean instance after the initialization method of the bean instance is invoked by the Spring container.

The Ordered interface defines a getOrder method which returns an integer value. The integer value returned by the getOrder method determines the priority of a BeanPostProcessor implementation with respect to other BeanPostProcessor implementations configured in the application context XML file. A BeanPostProcessor with higher order value is considered at a lower priority, and is executed after the BeanPostProcessor implementations with lower order values are executed.

The following example listing shows bean definitions for InstanceValidationBeanPostProcessor class:

Example listing – InstanceValidationBeanPostProcessor bean definition

<bean class="…...springbankapp.postprocessor.InstanceValidationBeanPostProcessor">

  <property name="order" value="1" />
</bean>

In the above bean definition, <bean> element’s id attribute is not specified because we typically don’t want InstanceValidationBeanPostProcessor to be a dependency of any other bean. The <property> element sets the value of order property to 1.

You can download the example code for this article from here. To run the example, execute the main method of SpringBankApp class.

Published at DZone with permission of its author, Ashish Sarin.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Liam Knox replied on Wed, 2012/12/12 - 10:14pm

This seems a pretty bad example of BeanPostProcessor usage and actually showing some static creation code for a database connection in a Spring book would also turn me away from this book immediately.

You would be far better demonstrating something like JMX like exposure using Bean PostProcessors rather than some validation!? behavior that could equally fit better in Spring lifecycle integrations such as @PostContruct.



Ashish Sarin replied on Wed, 2012/12/12 - 10:54pm in response to: Liam Knox

I would like to mention that the section on BeanPostProcessor and BeanFactoryPostProcessor is explained in chapter 4 of the book. The database interaction using Spring's JDBC and ORM modules is explained in chapter 6 of the book. For this reason, we chose an example with which a reader can easily connect. Introducing a JMX-based example earlier on would mean introducing readers to JMX.

Even though the code shows database interaction, the sample application of chapter 4 doesn't interact with any database. Again, for the sake of understanding, the sample application simply shows the controller, service and DAO layers of the application.


regards

ashish

Liam Knox replied on Thu, 2012/12/13 - 12:20am in response to: Ashish Sarin

 I said JMX like i.e. exposing something not showing an example what is a more natural fit for a life cycle callback like @PostConstruct. That said JMX is a distinct technology independent of Spring so most non Spring Java developers would be familar and it is a perfect BeanPostProcessor use case. 

Though showing static lookups in Spring.... it's a bit like demonstrating how to hold a Gun by showing a child with a Gun in his or her mouth J


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.