Enterprise Java

How to use Events in Spring 3.x

There are many concepts and techniques for creating loosely coupled applications, Event is one of them. Events can eliminate many of dependencies in your code. Some times without events, SRP* is very hard to implement. Observable interface in java can help us to implement events (through Observer Pattern).

But wait, the goal of this post is a fast tutorial about Spring Event. Spring has some nice facilities for creating Event Driven Applications. You can raise a specific event in a bean and listen to it in the other bean.

Imagine a simple application with these requirements:
 

  • There are some orders that can have different status
  • when an order is in DELIVERED or POSTPONED state we need to send an email to the customer

The first (but not the best) solution for requirement satisfaction is sending email in our Order model, But There are some defects:

  • It’s not responsibility of Order to sending email.
  • If you follow Domain Driven principle, Order is a domain object but Email Sender maybe is a service ( different from Domain Service ) so you can’t use it in your model.

The other solution is raising some events in our Order model after changing its status. I’s not concern of Order what will happen after raising this event. In our example we need to listen to a specific kind of events, analyze them and do some business (Sending Email).

@Configurable
 public class Order implements ApplicationEventPublisherAware {
 private final String orderId;
 private final Date createDate;
 private final CustomerInfo customerInfo;
 private ApplicationEventPublisher eventPublisher;
 private Date lastUpdateDate;
 private Status status;
 
public Order(String orderId, CustomerInfo customerInfo) {
 this.orderId = orderId;
 this.customerInfo = customerInfo;
 status = Status.MODIFIABLE;
 this.createDate = new Date();
 this.lastUpdateDate = this.createDate;
 }
 
public String getOrderId() {
 return orderId;
 }
 
public void checkOut() {
 if (status == Status.DELIVERED) {
 throw new IllegalStateException(String.format("Order is already delivered"));
 }
 this.status = Status.CHECKED_OUT;
 this.lastUpdateDate = new Date();
 }
 
public void deliver() {
 if (this.status != Status.CHECKED_OUT && this.status != Status.POSTPONED) {
 throw new IllegalStateException(String.format("Order status should be CHECKED OUT for delivery to be called. but is : %s", status));
 }
 
this.status = Status.DELIVERED;
 this.lastUpdateDate = new Date();
 this.eventPublisher.publishEvent(new OnOrderDelivered(this));
 }
 
public void postponeDelivery() {
 if (status != Status.CHECKED_OUT && status != Status.POSTPONED) {
 throw new IllegalStateException(String.format("Can not postpone delivery in this state: %s", status));
 }
 this.status = Status.POSTPONED;
 this.lastUpdateDate = new Date();
 this.eventPublisher.publishEvent(new OnOrderPostponed(this));
 }
 
public Status getStatus() {
 return status;
 }
 
public CustomerInfo getCustomerInfo() {
 return customerInfo;
 }
 
public Date getLastUpdateDate() {
 return lastUpdateDate;
 }
 
public Date getCreateDate() {
 return createDate;
 }
 
@Override
 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
 this.eventPublisher = applicationEventPublisher;
 }
 
public static enum Status {
 MODIFIABLE,
 CHECKED_OUT,
 POSTPONED,
 DELIVERED,
 CANCELED;
 }
 }

As you see Order is a configurable class, if you have not worked with this concept before, don’t be anguished. For this post just you need to know Configurable classes can create everywhere with new keyword but they are managed by spring, so you can inject other beans in them or use most of the spring facilities with them. I promise to post an article about it as soon as possible.

Order class implements  org.springframework.context.ApplicationEventPublisherAware interface. This interface has a setter method with name setApplicationEventPublisher that presents ApplicationEventPublisher for using in your class. As you see in deliver method we used this object to publish an event (this.eventPublisher.publishEvent(new OnOrderDelivered(this))). You can publish every event that extends org.springframework.context.ApplicationEvent. We have raised another event OnOrderPostponed when an order becomes postponed.

public abstract class OnOrderStatusChanged extends ApplicationEvent {
 private final Order order;
 
public OnOrderStatusChanged(Order source) {
 super(source);
 this.order = source;
 System.out.println(String.format("Order:%s status is changed to %s", source.getOrderId(), source.getStatus()));
 }
 
public Order getOrder() {
 return order;
 }
 }
 
public class OnOrderDelivered extends OnOrderStatusChanged {
 public OnOrderDelivered(Order order) {
 super(order);
 }
 }
 
public class OnOrderPostponed extends OnOrderStatusChanged {
 public OnOrderPostponed(Order order) {
 super(order);
 }
 }

OnOrderStatusChanged is an abstract class what OnOrderDelivered and OnOrderPostponed extends it. until now we could create our events and raise them. now if you create a spring test and call deliver method of an order you will see “Order:X status is changed to DELIVERED” The last step is doing something when these events published. we want to send an email to the customer when these method raised. additionally it’s valuable for customer to posting the product when his order is in delivered state. Listeners are simple Beans that implements generic ApplicationListener interface. Parameter type in this interface is the type of event that you want to listen to it. it is possible to defince parameter type as the parent and listen to all of its childres. for example in our model if we use OnOrderStatusChanged our listener will catch all event from OnOrderDelivered and OnOrderPostponed

It can be suitable for sending email in our scenario. but we don’t use this model and create two different listener for them.

As you see below their code is very simple

@Service
 public class OrderDeliveredEmailSender implements ApplicationListener,Ordered {
 
@Override
 public void onApplicationEvent(OnOrderDelivered event) {
 System.out.println(String.format("Message sent for delivered order to:%s ORDER-ID:%s",event.getOrder().getCustomerInfo().getEmail(),event.getOrder().getOrderId()));
 }
 
@Override
 public int getOrder() {
 return 100;
 }
 }
 
@Service
 public class OrderPostponedEmailSender implements ApplicationListener {
 
@Override
 public void onApplicationEvent(OnOrderPostponed event) {
 System.out.println(String.format("Message sent for postponed order to:%s ORDER-ID:%s", event.getOrder().getCustomerInfo().getEmail(), event.getOrder().getOrderId()));
 }
 }

These two beans will fire onApplicationEvent when  correspond event is raised. For posting product to the customer we need to create another Listener for OnOrderDelivered event.

@Service
 public class OnOrderDeliveredPost implements ApplicationListener,Ordered {
 @Override
 public void onApplicationEvent(OnOrderDelivered onOrderDelivered) {
 System.out.println(String.format("Order:%s is posting for customer.",onOrderDelivered.getOrder().getOrderId()));
 }
 
@Override
 public int getOrder() {
 return 1000;
 }
 }

As you see this listener will send product to the customer when its state is DELIVERED. But wait there what is Ordered interface? if you have not use org.springframework.core.Ordered interface, it is valuable to know that with using this interface you can define the order between beans in a collection. in our scenario customer like to receive an email before we post the product to him. for this purpose these two class implement Ordered interface, don’t forget the lowest order has the highest priority.

*Single Responsibility Principle

 

Reference: How to use Events in Spring 3.x from our JCG partner Soroosh Sarabadani at the Just another Java blog blog.

Soroosh Sarabadani

A worm who loves to have JAVA and share his findings with others.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Kristianto Yanuar
Kristianto Yanuar
10 years ago

Very nice article! Very helpful!

Jety
10 years ago

Thank you very much, man, I am going to implement Spring event firing/listening and the information you have here is exactly what I need.

RISHI
RISHI
9 years ago

i tried to run the apps after getting the code but unit test failed and when i debugged i am getting null pointer for eventpublisher.

sunny
9 years ago

very good pg

Prabhu
Prabhu
9 years ago

My entire search ended here. The level of detail provided in this blog helped me to understand and relate exactly with my scenario. Thanks a ton!

Back to top button