March 12, 2017

#Spring part 7: Event Handling in Spring

Sometimes in our spring application, we may want to add capability of listening specific events so that we can process these events as per application logic.

We can do this by writing the event processing code as another method within existing application code, but then it will be tightly coupled with our existing code and we will not have much handle to change it later (we might need to disable it for certain duration or permanently remove it).

But, if we can configure these event processors through our application context file, then we need not to change our application code as well as event processor code in most circumstances. We just need to switch off event processing, or if we want add another event processor for that event the all we need to do it change our context file configuration.

In spring event-based communication model, the sender component just publishes an event without knowing who the receiver will be. Also, the receiver needn’t know who is publishing the event. Listener can listen to multiple events from different senders at the same time. In this way, the sender and receiver components are loosely coupled.

Writing a Listener class:
Our Listener class need to implement ApplicationListener interface and handle the events in the onApplicationEvent() method. onApplicationEvent() is called by spring when any application event is published.

package com.test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventListner implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("Inside MyEventListner..."+event.toString());       
    }
}


We have used @Component annotation in MyEventListner to register it as a valid event listener. The package in which MyEventListner is declared need to registered for component scanning, for that we need to add below line in spring.xml:

< context:component-scan base-package="com.test" / >

MyEventListner will catch all the framework related events, we must filter the events by ourself.

If you want, you can write separate Listeners for event start and stop, e.g:

@Component
public class MyEventStartListner implements ApplicationListener {
    @Override
    public void onApplicationEvent(
ContextStartedEvent startevent) {
        System.out.println("Inside MyEventListner: Event is started.."+
startevent.toString());       
    }
}


@Component
public class MyEventStopListner implements ApplicationListener {
    @Override
    public void onApplicationEvent(
ContextStoppedEvent stopevent) {
        System.out.println("Inside MyEventListner: Event is stopped.."+
stopevent.toString());       
    }
}

How to publish our own event?
We need to have our own event class, by extending ApplicationEvent class

package com.test;
import org.springframework.context.ApplicationEvent;
public class DrawEvent extends ApplicationEvent{
    public DrawEvent(Object source) {
        super(source);
    }
    @Override
    public String toString() {       
        return "DrawEvent occurred";
    }
}


Now we need to register this event in our bean (here in our case Circle bean). We need to implement ApplicationEventPublisherAware interface, so that we can use ApplicationEventPublisher‘s publishEvent() method to send event to listeners. Here we want to send event whenever draw method is called, so inside draw9), we will call publisher.publishEvent() and passing the instance of event which need to be called (in our case its DrawEvent)

package com.test;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

@Component
public class Circle implements ApplicationEventPublisherAware {   
    private ApplicationEventPublisher publisher;
   
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher=publisher;
    }
   
    public void draw() {
        System.out.println("Drawing circle...");
        DrawEvent drawevent=new DrawEvent(this);
        publisher.publishEvent(drawevent);
    }
}


Make a small change in MyEventListner, add a filter so it can catch only DrawEvent

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventListner implements ApplicationListener < DrawEvent > {
    @Override
    public void onApplicationEvent(DrawEvent event) {
        System.out.println("Inside MyEventListner..."+event.toString());       
    }
}


Now whenever, draw() method is called, MyEventListner will be called. The output of below call will be "Inside MyEventListner...DrawEvent occurred"

AbstractApplicationContext  context=new ClassPathXmlApplicationContext("beanPostProcessorSpring.xml");
Circle circle=(Circle)context.getBean("circle");
circle.draw();


-K Himaanshu Shuklaa..

No comments:

Post a Comment