Core Spring : Event Handling

Spring allow you to handle events triggered by the framework. An Event is triggered because of the state change or some activities with in the container. For eg, Spring triggers ContextClosedEvent when the ApplicationContext is closed. If you want to do some house keeping activities on events, then you can listen to those events through a listener and do your stuff. Spring event mechanism is based on the standard Observer design pattern.

There are 3 core components to event handling in general – Event Publisher, Event Listner and lastly the Event itself. Spring provides ApplicationEvent class and ApplicationListener interface to support event handling. If you want to handle an event, then create a bean by implement ApplicationListener interface and plugin that bean to the ApplicationContext. Spring will recognize such beans and everytime an event (which is a sub class of ApplicationEvent) is published to the ApplicationContext, your bean will be notified. We can create beans that listen for events which are published through our ApplicationContext. This is achieved via the ApplicationEventPublisher interface.

Lets create an Listner class that will listen to framework events- MyEventListener.java


package mynotes.corespring.events;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventListener implements ApplicationListener {

public void onApplicationEvent(ApplicationEvent event) {
System.out.println(event.toString());
}

}

spring.xml:


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<context:annotation-config/>
<context:component-scan base-package="mynotes.corespring.events"/>

</beans>

App.java:


package mynotes.corespring.events;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {

AbstractApplicationContext context=new ClassPathXmlApplicationContext("/mynotes/corespring/events/spring.xml");
context.registerShutdownHook();

}

}

Above we are simply creating a context and registering a shutdown hook that will close the context automatically.

Output:


org.springframework.context.event.ContextRefreshedEvent; root of context hierarchy]

org.springframework.context.event.ContextClosedEvent; root of context hierarchy]

Notice the 2 events captured by the listner – ContextRefreshedEvent and ContextClosedEvent.

You can create your own events in Spring by extending the class ApplicationEvent. You can then publish such events through ApplicationContext or ApplicationEventPublisher.

Lets create a custome event:


package mynotes.corespring.events;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent{

private String message;

public MyEvent(Object source,String message) {
super(source);
this.message=message;
}
@Override
public String toString() {
return "MyEvent occured";
}

public String getMessage() {
return message;
}

}

We need to extent the ApplicationEvent class of spring. It will force you to have a constructor which takes an Object and then call super passing that object. In our case we have a constructor with Object source as well as a String message. We overrided the toString method to know if an event was publish or not, which will be noticed by the Listner.

Lets create a Publisher. Spring provides an interface ApplicationEventPublisher which has a method publish(Event) that takes an event that needs to be publish. Now whatever class is the source of publishing, we implemets an interface called ApplicationEventPublisherAware,  and implement the setApplicationEventPublisher() method to set the publisher to ApplicationEventPublisher local variable. MyEventPublisher.java


package mynotes.corespring.events;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

@Component
public class MyEventPublisher implements ApplicationEventPublisherAware {

private ApplicationEventPublisher publisher = null;

public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher=applicationEventPublisher;
}

public void myPublisher(String message){
publisher.publishEvent(new MyEvent(this, message));
}

}

Changing App.java


package mynotes.corespring.events;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {

AbstractApplicationContext context=new ClassPathXmlApplicationContext("/mynotes/corespring/events/spring.xml");
context.registerShutdownHook();
MyEventPublisher myEventPublisher=(MyEventPublisher) context.getBean("myEventPublisher");
myEventPublisher.myPublisher("Publish this message");
myEventPublisher.myPublisher("Publish another message");
}

}

Output:


org.springframework.context.event.ContextRefreshedEvent; root of context hierarchy]
MyEvent occured
MyEvent occured
org.springframework.context.event.ContextClosedEvent; root of context hierarchy]

Notice that the listner captured our custom event. Also, it is capturing all events of both container (ContextRefereshedEvent and ContextClosedEvent) and our custom event (MyEvent). This is because we did not provide any particular generic type to our Listner class. Changing MyEventListener.java


package mynotes.corespring.events;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventListener implements ApplicationListener<MyEvent> {

/*public void onApplicationEvent(ApplicationEvent event) {
System.out.println(event.toString());
}*/

public void onApplicationEvent(MyEvent event) {
System.out.println(event.toString());

}

}

Now running App.java will capture only MyEvent


MyEvent occured
MyEvent occured

One of the advantages to using Spring’s event publishing (observer pattern) is that the components are loosely coupled – there is no direct coupling between the publisher and the subscriber. Sure, they both have a dependency on the Spring Event API, but not on each other. This makes it possible to extend the application by adding/removing subscribers without affecting other subscribers.

Using Events are a good fit when:

  • you might in future need to take more than one independent action when the event occurs
  • the processing needs to be handed off to another thread to prevent blocking, e.g. sending an email (using a custom ApplicationEventMulticaster).

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: