Yohan is a Senior Technical Lead specialising in Java and related technologies. His interests are in Software Architecture, API Design and Development, Enterprise Integration, Messaging, Distributed Computing and Parallel computing. Yohan is a DZone MVB and is not an employee of DZone and has posted 16 posts at DZone. You can read more from them at their website. View Full User Profile

Eventing with Spring Framework

10.11.2012
| 12452 views |
  • submit to reddit

Spring Framework, since it’s inception, included an eventing mechanism which can be used for application-wide eventing. This eventing mechanism was developed to be used internally by Spring Framework for eventing, such as notification of context being refreshed, etc, but it can be used for application specific custom events as well. This eventing API is based on  an interface named org.springframework.context.ApplicationListener, which defined one method named onApplicationEvent. Below code snippet shows a simple events listener which just logs the event information.

package com.yohanliyanage.blog.springevents;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class MyEventListener implements ApplicationListener {
    private static final Log LOG = LogFactory.getLog(MyEventListener.class);
    
    public void onApplicationEvent(ApplicationEvent event) {
        LOG.info("Event Occurred : " + event);
    }
}

To register this event listener, all that we have to do is to add it as a Spring managed bean. If we just add it as a bean in Spring Bean Configuration XML, or if we have annotation scanning enabled, adding an annotation such as @Component, would ensure that our listener will receive events via Spring. Below XML block shows the simple bean definition in XML for registering this listener.

< ?xml version="1.0" encoding="UTF-8"?>
<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.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Adding the listener class to Spring Container automatically registers it for events -->
	<bean class="com.yohanliyanage.blog.springevents.MyEventListener" />
	
</beans>

Now, to test this code, let’s write up a main method which creates the Spring Application Context. In this code, it’s assumed that the Spring bean definition file is located at META-INF/spring/application-context.xml, which is in class path. You can download the sample code here.

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context =
            new ClassPathXmlApplicationContext("classpath:META-INF/spring/application-context.xml");
    }
}

When we run this, we get the following output.

18:45:00 INFO Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a982589: startup date [Sun Sep 30 18:45:00 IST 2012]; root of context hierarchy
18:45:00 INFO Loading XML bean definitions from class path resource [META-INF/spring/application-context.xml]
18:45:00 INFO Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@33e228bc: defining beans [com.yohanliyanage.blog.springevents.MyEventListener#0]; root of factory hierarchy
18:45:00 INFO Event Occurred : org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@7a982589: startup date [Sun Sep 30 18:45:00 IST 2012]; root of context hierarchy]

As highlighted above, our listener gets notified by the framework when Spring Context is refreshed during initialization. This is a framework event, and of course, there’s no magic to it. But how can we generate application specific, custom events? As you will see in the blow code block, this is also very simple and straight-forward.

First, let’s create our own event implementation. For this, we just have to write a class that extends from ApplicationEvent.

package com.yohanliyanage.blog.springevents;

import org.springframework.context.ApplicationEvent;

public class MyCustomEvent extends ApplicationEvent {

	private static final long serialVersionUID = -5308299518665062983L;

	public MyCustomEvent(Object source) {
		super(source);
	}
}

Next, we have to write a class which does the event publishing. In order to publish an event, we need to get a reference to ApplicationEventPublisher. This can be easily done by implementing the ApplicationEventPublisherAware, as shown in the code block below. 

package com.yohanliyanage.blog.springevents;

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

public class MyEventPublisher implements ApplicationEventPublisherAware {

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

	public void publish() {
		this.publisher.publishEvent(new MyCustomEvent(this));
	}
}

Now, this bean also should be added to the Spring bean configuration, as follows. Note that you do not have to write a separate publisher class in your application. Your existing Spring beans can easily to this by just implementing the ApplicationEventPublisherAware interface.

<bean class="com.yohanliyanage.blog.springevents.MyEventPublisher" />

Now, let’s slightly modify the Main class to invoke the publish() method in our event publisher.

	public static void main(String[] args) throws InterruptedException {
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/application-context.xml");
		
		MyEventPublisher publisher = context.getBean(MyEventPublisher.class);
		publisher.publish();
	}

When we run the application now, we get the following output.

19:21:18 INFO Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a982589: startup date [Sun Sep 30 19:21:18 IST 2012]; root of context hierarchy
19:21:18 INFO Loading XML bean definitions from class path resource [META-INF/spring/application-context.xml]
19:21:19 INFO Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2565a3c2: defining beans [com.yohanliyanage.blog.springevents.MyEventListener#0,com.yohanliyanage.blog.springevents.MyEventPublisher#0]; root of factory hierarchy
19:21:19 INFO Event Occurred : org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@7a982589: startup date [Sun Sep 30 19:21:18 IST 2012]; root of context hierarchy]
19:21:19 INFO Event Occurred : com.yohanliyanage.blog.springevents.MyCustomEvent[source=com.yohanliyanage.blog.springevents.MyEventPublisher@5a676437]

As highlighted above, our custom event has been triggered, and our listener was able to handle that event.

So far, so good. But if you have noticed, our listener gets invoked for all of the events that occurs in the application, including framework events. But in most of the cases, this is not desirable. The listener will be interested in one or more specific events. Up until Spring 3.0, this eventing mechanism did not had support for filtering events. That is, if you implement an ApplicationListener, you would end up receiving all events that occurs in the application, and you had to manually look into the ApplicationEvent object that gets passed in to your listener to identify and discard events that you are not interested in. This of course, was a hassle, and probably due to this, Spring Eventing did not get attention of most of the developers.

With Spring 3.0, this API was enhanced with Generics support, to provide filtering of events. Now, the ApplicationListener interface is parameterized as follows.

public interface ApplicationListener < E extends ApplicationEvent > extends EventListener

The onApplicationEvent method parameter uses generic type E. With this, we can implement our listener as follows.

package com.yohanliyanage.blog.springevents;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationListener;

public class MyEventListener implements ApplicationListener < MyCustomEvent > {

	private static final Log LOG = LogFactory.getLog(MyEventListener.class);
	
	public void onApplicationEvent(MyCustomEvent event) {
		LOG.info("Event Occurred : " + event);
	}

}

This listener implementation’s onApplicationEvent method will be called only for MyCustomEvent based events. This in turn provides the necessary event filtering, where we don’t have to write boilerplate code to discard unnecessary events. The log output is as follows.

19:29:31 INFO Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a982589: startup date [Sun Sep 30 19:29:31 IST 2012]; root of context hierarchy
19:29:31 INFO Loading XML bean definitions from class path resource [META-INF/spring/application-context.xml]
19:29:31 INFO Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2565a3c2: defining beans [com.yohanliyanage.blog.springevents.MyEventListener#0,com.yohanliyanage.blog.springevents.MyEventPublisher#0]; root of factory hierarchy
19:29:31 INFO Event Occurred : com.yohanliyanage.blog.springevents.MyCustomEvent[source=com.yohanliyanage.blog.springevents.MyEventPublisher@5a676437]

As seen above in the output, we no longer receive the unwanted framework specific events, or any other events that we are not interested in.

So in conclusion, Spring does provide a decent eventing mechanism which is quite useful for implementing eventing support in applications. While this has been around since the early days of Spring, it did not see wide adoption primarily due to it’s incapability of filtering out specific events for a listener. But with Spring 3.0, things are improved, and now it has reached a state where we can leverage it to broadcast events in our applications with ease. One thing to note is that by default, Spring Eventing is synchronous. But this can be made asynchronous by providing a custom ApplicationEventMulticaster implementation that would make use of a TaskExecutor.

 

 

 

 

 

 

 

 

 

 

 

 

Published at DZone with permission of Yohan Liyanage, author and DZone MVB. (source)

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

Comments

Daniel Kvasnička replied on Fri, 2012/10/12 - 2:00am

So yesterday we've read an article on this website that bashed Java EE and tried to say that it's actually over, that Spring is the silver bullet. Not only was the article completely off-the-rails but today, we are served THIS... oh my, thanks God for CDI/EE6 and it's luxurious eventing framework!

Yohan Liyanage replied on Sun, 2012/10/14 - 7:02am in response to: Daniel Kvasnička

Just to set things on perspective, Spring is no silver-bullet (my personal opinion). In fact, any seasoned developer knows that there's no technology that fits for all. This applies to both Spring, and Java EE. 

I have been developing using both Spring and Java EE for a long time, and both has it's own pro's and con's.  In fact, Java EE is (at least partly) what it is today (compared to old days of J2EE) because of the concepts popularized by Spring.   

I totally agree with you that CDI has a nice eventing facility. However, one thing to note is that it does not address the needs of everybody. Not all applications are Java EE applications (think of standalone). Even if it is a Java EE application, not everybody has the luxoury of using the latest & greatest, at least in real enterprise world. Also, if it is indeed a Java EE application, there are much better ways to achieve eventing (better than Spring & also CDI Eventing), but that of course depends on the use case. 

So in a nutshell, the idea behind this article is that if you have an application built on Spring, and if you are looking at options to have a basic eventing support, then you could have a look at what Spring has to offer. If the application is built on top of EE 6, then CDI is another option. But neither is a silver-bullet.

P.S. - This website is partly a blog aggregator. So the article you read yesterday has no connection to this. 

Comment viewing options

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