Spring boot Event Handling

Any event handling mechanism involves the following components :

  1. An Event itself.
  2. A listener which performs action when the event occurs.
  3. An activity which is responsible for event generation such as a mouse click, record added, deleted and so on.
  4. A helper object (optional) involved in event chain.
Scenario
A typical example of an event is a user changing his password.

When a user changes his password, an e-mail is sent to his registered e-mail id informing of the password change.
This scenario involves password changing logic and e-mail sending logic which, in a well designed application obviously, will be written in different places.

It is also possible that the application sends e-mail for various other events as well.

Thus the presence of event handling mechanism is a necessary requirement.
In its absence, calling a logic written at one place (e-mail sending logic for example) from multiple places becomes a tedious task and creates a tight coupling between code fragments.

Luckily, Spring supports an event handling mechanism where all the event modules (event, listener, publisher etc.) are all written as separate components and thus are loosely coupled making the development and maintenance of application much easier.

Spring’s Event Handler Framework

Relating to the components of event handling given at the start of this post, Spring’s event handler model consists of:
1. An Event
An event in Spring is a class which  extends org.springframework.context.ApplicationEvent class (A User in above case).

2. A listener class
A listener class in Spring is identified as a class which implements org.springframework.context.ApplicationListener.
This class contains the action to be performed after the event occurs such as sending e-mail after the password is changed in our case.

3. An activity
Which generates an event (changing password in above case). Activity is enclosed in a class which implements org.springframework.context.ApplicationListener interface.
Based on the above explanation, let’s look at the code implementing event handling in Spring.

As before, the code is divided into components used in event handling flow.

The Event

public class PasswordChangeEvent extends ApplicationEvent {
   String eventName;
   public String getEvent() {
      return eventName;
   }
   public void setEvent(String event) {
      this.eventName = event;
   }
   public PasswordChangeEvent(Object source, String eventType) {
      super(source);
      eventName = eventType;
   }
 }

First step in an event flow is the event itself. This is a Spring event as it extends ApplicationEvent class.

When a class extends ApplicationEvent, it is forced to provide a constructor as ApplicationEvent lacks a no-arg constructor.

The Listener Class
Listener class is marked by implementing org.springframework.context.ApplicationListener interface.

This interface accepts generic argument denoting the event type for which it listens so that it is invoked for matching events only Example,

public class CustomEventListener implements ApplicationListener<E> {
    @Override
    public void onApplicationEvent(E event) {

    }
 }

Notice the generic argument at interface and argument supplied to onApplicationEvent method are same.

The event may be a framework event such as context initialized or any custom event such as Password Change event, as in this case.

ONLY
condition is that it should extend ApplicationEvent.

ApplicationListener interface has just one method onApplicationEvent() which accepts the event for which it listens.
Any class implementing this interface must also implement this method.

Example of event listener is given below.

import org.springframework.context.ApplicationListener; 

public class PasswordChangeEventListener implements ApplicationListener { 
   @Override
   public void onApplicationEvent(PasswordChangeEvent event) { 
      System.out.println(event.getEvent() + " at " + event.getTimestamp()); 
   }
}

Listener class must be registered as a bean in Spring Application Context.
For XML based configuration, it must be declared as a bean in configuration file as :

<bean id="listener" class="PasswordChangeEventListener" ></bean>

or for Annotation based configuration, annotate it with @Component annotation.

The Activity
This is the class where the event for which we want to take an action resides.

This class MUST implement org.springframework.context.ApplicationEventPublisherAware interface and thus implement its setApplicationEventPublisher method as shown below.

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

public class UserOperations implements ApplicationEventPublisherAware {
       
  /**
   * Framework's object 
   */
  ApplicationEventPublisher eventPublisher;
  
  /**
   *  Activity which will generate an event 
   */
  public void changePassword() {
    eventPublisher.publishEvent(new PasswordChangeEvent(this, "Password Changed"));
  }

  @Override
  public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    eventPublisher = applicationEventPublisher;
  }
}

setApplicationEventPublisher() provides a reference to framework’s ApplicationEventPublisher object which can be used to call its publishEvent() method.

What publishEvent() does is, it takes an object of ApplicationEvent (or its sub-class; PasswordEvent, in our case) and notifies ALL listeners listening for this event type whenever this method (publishEvent()) is called and invokes their onApplicationEvent()method (defined in PasswordChangeEventListener, in our example).

In order to be discovered, this class must be registered as a bean in Spring Application Context.
For XML based configuration, it must be declared as a bean in configuration file as :

<bean id="operations" class="UserOperations" ></bean>

or for Annotation based configuration, annotate it with @Component annotation.

Now, let’s test our event mechanism with a Main class.

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

public class EventHandling {
   public static void main(String[] args) {
      // get the application context
      ApplicationContext context = 
   new ClassPathXmlApplicationContext("classpath:spring-core-config.xml");
      // get the bean where password change logic is defined
      UserOperations userOperations = context.getBean(UserOperations.class);
      // change password
      userOperations.changePassword();
  }
}

The output is as expected :

Password Changed

Finally, a quick walk-through of the execution flow to grasp the topic in a firm way so that it can be implemented in no-time when required.

  1. Create an event class which extends ApplicationEvent.
  2. Create a listener class which implements ApplicationEventListener and provide an action which needs to be taken when the event occurs in its onApplicationEvent method.
  3. Create a class which contains the method that generates the event.
    This class should implement ApplicationEventPublisherAware interface and must implement its setApplicationEventPublisher method to get a reference to Spring’s ApplicationEventPublisher object.
    Then, from the method which generates the event call publishEvent method on ApplicationEventPublisher object passing it the event object created in Step 1.

Let’s tweak in

1. ApplicationEvent has a single constructor which accepts an java.lang.Object argument which marks as the source of the event.

2. ApplicationEvent has a timestamp field which marks the time at which the event occurred and may be retrieved using getTimeStamp() method of the event object extending this class.

3. ApplicationListener interface’s onApplicationEvent() method accepts a generic type object and marks the event for which it listens.
The object passed to this method must extend  ApplicationEvent class.

4. The type of parameter accepted by onApplicationEvent() is the same which is accepted by ApplicationListener.
Thus, if ApplicationListener is declared as
ApplicationListener<PasswordChangeEvent>, then this method will be implemented as onApplicationEvent(PasswordChangeEvent event)

5. onApplicationEvent() method of ApplicationListener is a callback method and is invoked automatically when an event, related to the object for which it is listening, occurs.

6. setApplicationEventPublisher() method is a callback method and is automatically invoked by Spring after the class where it is defined (and which extends ApplicationEventPublisherAware) is initialized.
For automatic initialization of bean refer this post.

Hope the article was useful.

Leave a Reply