Learn to use create publisher-subscriber based events in a Spring application. Spring has inbuilt support for creating application events, publishing them and then listening to them in event handlers. There are a few simple guidelines to follow to create/listen to application events:
- The event should extend
ApplicationEvent
- The publisher should inject an
ApplicationEventPublisher
object - The listener should implement the
ApplicationListener
interface
1. Spring Events
Sometimes in our application, we may want to add the capability for listening to specific application events so that we can process these events for various audit purposes. Examples of these events can be when an employee is added/ deleted, or a certain kind of transaction is completed or rolled back.
We can always write the event processing code as another method within the existing application code, BUT then it will be tightly coupled with your existing code, and we will not have much handle to change it later (suppose you don’t want to process those events for a certain duration).
If you can configure event processors through the application context file, then we need not change your application code as well as the event processor code in most circumstances. Any time we need to switch off event processing or add another event processor for that event, all you need to do is change your context file configuration, and that’s it. Sounds good !!
In the spring event-based communication model, the sender component just publishes an event without knowing who the receiver will be. Actually, there may be more than one receiver component. Also, the receiver needn’t know who is publishing the event. Listeners can listen to multiple events from different senders at the same time. In this way, the sender and receiver components are loosely coupled.
Let’s learn how we can achieve this publish and listen events in a spring application.
2. Publishing Spring Events
2.1. Creating Event Class
Till Spring 4.2, all event classes had to extend the ApplicationEvent
class.
public class EmployeeEvent extends ApplicationEvent
{
private String eventType;
private EmployeeDTO employee;
//constructors and getters
}
With later versions of Spring, extending the ApplicationEvent is not mandatory.
public class EmployeeEvent
{
private String eventType;
private EmployeeDTO employee;
//constructors and getters
}
2.2. Event Publisher
To publish the event, the publisher can simply inject the ApplicationEventPublisher and use the publishEvent() API. The publishEvent() API accepts the method argument of type Object so extending the ApplicationEvent class is not necessary anymore.
@Service ("employeeManager")
public class EmployeeManagerImpl implements EmployeeManager
{
@Autowired
private EmployeeDAO dao;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public EmployeeDTO createNewEmployee()
{
EmployeeDTO employee = dao.createNewEmployee();
publisher.publishEvent(new EmployeeEvent(this, "ADD", employee)); //publish event
return employee;
}
}
Before Spring 4.2, any bean, which implements ApplicationEventPublisherAware
interface, could have used its publishEvent()
method to send events to the registered listeners.
@Service ("employeeManager")
public class EmployeeManagerImpl implements EmployeeManager, ApplicationEventPublisherAware
{
@Autowired
private EmployeeDAO dao;
private ApplicationEventPublisher publisher;
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public EmployeeDTO createNewEmployee()
{
EmployeeDTO employee = dao.createNewEmployee();
publisher.publishEvent(new EmployeeEvent(this, "ADD", employee)); //publish event
return employee;
}
}
3. Creating Event Listener
3.1. Using ApplicationListener
To listen to certain events, a bean must implement the ApplicationListener
interface and handle the events in the onApplicationEvent()
method. Actually, Spring will notify a listener of all events, so you must filter the events by yourself.
If we use generics, Spring will deliver only messages that match the generic type parameter. In this example, we are using generics code to listen only EmployeeEvent
.
@Component
public class EmployeeEventsProcessor implements ApplicationListener<EmployeeEvent>
{
public void onApplicationEvent(EmployeeEvent event)
{
EmployeeEvent employeeEvent = (EmployeeEvent) event;
System.out.println("Employee " + employeeEvent.getEventType()
+ " with details : " + employeeEvent.getEmployee());
// Do more processing as per application logic
}
}
3.2. @EventListener Annotation
Since Spring 4.1, we can simply annotate a method with @EventListener
and register the event listener of the type method argument.
@Component
public class EmployeeEventsProcessor {
@EventListener
void handleEmployeeEvent(EmployeeEvent event) {
// handle the event
}
}
Note that event type can be defined in the annotation itself. Also, we can register multiple events to a single method.
@Component
public class EmployeeEventsProcessor {
@EventListener(EmployeeEvent.class)
void handleEmployeeEvent() {
// handle the event
}
@EventListener({ApplicationStartEvent.class, ApplicationStopEvent.class})
void handleApplicationEvent() {
// handle the event
}
}
If the event handler method is non-void, Spring will create a new Event for the returned value.
@Component
public class EmployeeEventsProcessor {
@EventListener(EmployeeEvent.class)
AuditEvent handleEmployeeEvent() {
// handle the event
return new AuditEvent();
}
@EventListener(AuditEvent.class)
void handleAuditEvent() {
// handle the event
}
}
4. Demo
In the following example, we are creating an Employee that will trigger the application event.
public class TestSpringContext
{
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception
{
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmployeeController controller = context.getBean(EmployeeController.class);
controller.createNewEmployee();
}
}
Program output.
INFO: Loading XML bean definitions from class path resource [applicationContext.xml]
Employee ADD with details : Employee [id=1, firstName=Lokesh, lastName=Gupta, type=null]
Great. We are able to listen to the events now, and we can process them the way we want.
Please note that application context itself also publish container events such as
ContextClosedEvent
,ContextRefreshedEvent
, andRequestHandledEvent
. If any of the beans want to be notified of these events, they can implement theApplicationListener
interface.
Happy Learning !!