Tuesday 18 July 2017

Communication between POJOs in Spring / Event publishing mechanism in Spring.



Sometimes we want beans to be able to communicate among themselves. This is typically done by Sender bean having access to Receiver bean, and then sender sends a message to receiver by using the reference to the receiver. This creates a tight coupling between sender and receiver. Sender is aware of the receiver. 


When using IoC POJOs can communicate by interface than by implementation. This model helps reduce coupling. However it is efficient only when the sender has to communicate with 1 receiver. When the sender has to communicate with multiple receivers it has to call the receivers 1 by 1. 


In Spring 1 component can send a message to another component by publishing an event without knowing who the receiver is. Receiver is also not aware of the sender. Receiver typically listens to an event. It can listen to multiple events from different senders. In this way, sender and receiver are loosely coupled.


First you must create an event, by extending ApplicationEvent class. It is an abstract class to prevent instantiation thus disallowing clients to create instances of ApplicationEvent class, prohibiting them from publishing generic event. Spring notifies a listener of all events, so you must filter the events yourself. If you use generics then Spring delivers the message to listener that listens specifically that event.



Create and event by extending ApplicationEvent class. Give it a constructor that accepts an object that describes the event and pass that object to super class ApplicationEvent. Application class is abstract and does not have default constructor, so subclasses are forced to provide a 1 or more arg constructor.



public class CheckoutEvent extends ApplicationEvent
{
    private Date checkoutTime;
    public CheckoutEvent(Object source, Date checkoutTime)
    {
        super(source);
        this.checkoutTime = checkoutTime;
    }
     //… some more code specific to the event
}

Create an event publisher by implementing ApplicationEventPublisherAware interface. It is the interface to be implemented by any object that wishes to be notified of the ApplicationEventPublisher (typically the ApplicationContext) that it runs in. ApplicationEventPublisher encapsulates the even publishing mechanism.


In the event publisher method just create the event and publish it by using calling publishEvent(event) on applicationEventPublisher instance.

@Component(value = "checkoutEventPublisher")
public class CheckoutEventPublisher implements ApplicationEventPublisherAware
{
    private ApplicationEventPublisher applicationEventPublisher;
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher)
    {
        this.applicationEventPublisher = applicationEventPublisher;
    }
    public void checkout(Object shoppingCart)
    {
        System.out.println("Checking out");
        CheckoutEvent checkoutEvent = new CheckoutEvent(shoppingCart, new Date());
        applicationEventPublisher.publishEvent(checkoutEvent);
    }
}

Create a listener  by implementing ApplicationListener interface and overriding onApplicationEvent(ApplicationEvent) method. Add the code for processing the event in it.

You must create a bean for the listener, else it won’t be notified.



Generic listener (Can listen to only 1 event)
As of Spring 3.0, an ApplicationListener can generically declare the event type that it is interested in. When registered with a Spring ApplicationContext, events will be filtered accordingly, with the listener getting invoked for matching event objects only.
@Component
public class CheckoutEventListener implements  ApplicationListener<CheckoutEvent>
{
    @Override
    public void onApplicationEvent(CheckoutEvent checkoutEvent) {
       
        System.out.println("Checkout event occured at " + checkoutEvent.getCheckoutTime());
    }
}

Raw listener(Can listen to several events from several publishers, so you need to check the type of event first before handling it)

@Component
public class CheckoutEventListener implements  ApplicationListener
{
    @Override
    public void onApplicationEvent(ApplicationEvent event)
    {
        if(event instanceof CheckoutEvent)
            System.out.println("Checkout event occured at " + ((CheckoutEvent)event).getCheckoutTime());
    }
}

No comments:

Post a Comment