Skip to content

How to use Events in Spring 3.x

March 18, 2013

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

You can download source code from : https://github.com/psycho-ir/spring-event.git

About these ads

From → Spring

4 Comments
  1. Nick permalink

    Wow.That’s really nice.
    It was really helpful to me.
    Thanks in advance.

  2. Alborz permalink

    thank you so much , the concept is so useful.
    please upload the runnable sample too.

Trackbacks & Pingbacks

  1. Spring Configurable Magic | IT World Web

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: