How to implement event driven programming in Spring Boot?

There are several paradigms of programming:

Procedural ,Object Oriented , Functional , Event Oriented etc.

Each of them comes with their own benefits and disadvantages.

Most of the programming done in Java is Object Oriented since the language itself is object oriented.

But what if you want to do event driven programming or at the least implement part of your functionality event driven?

Event driven programming let’s you decouple business logic from the event source.

Say you want to send an email to user whenever they register , you can implement this in an event driven way moving the emailing part to “a listener” which listens to “register” event.

Later if you want you can register a different “listener” without much change in code.

Spring Boot provides this functionality through Spring Events.

Let’s see how to implement it.

We will create a dummy API to add a user to the system.

Let’s say there is a requirement to audit all service calls made to the system.

We can move the auditing part event driven.

You need to have three main components to implement Spring Events

  1. An Event class
  2. A Publisher method
  3. A Subscriber method

Let’s see how to implement them:

STEP1: Create an Event class

This is a simple POJO:

package com.example.spring.event;
public class UserEvent{
	private String name;
	public UserEvent(String name) {
		this.name = name;
	}
	
	
	public String getName() {
		
		return this.name;
	}
}

This represents the Add User event.

STEP2: Create a publisher method

We need to create a publisher method which will publish the above User Event.

To create this autowire ApplicationEventPublisher bean to your service class:

package com.example.spring.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class UserService {
	
	@Autowired
	private ApplicationEventPublisher publisher;
	
	
	public void addUser(String user) {
		
		
		System.out.println("Adding user "+user);
		
		
		this.publisher.publishEvent(new UserEvent("User Added"));
		
		System.out.println("Method ends");
	}
}

Whenever a user is added in our example , the publisher publishes an User Event as shown above. In the above example I am just passing a string to the User Event , you can also accept custom objects as per the use case.

STEP3: Create a listener method

Once an event is published there should be a listener to handle that event.

Create a method and annotate it with @EventListener annotation:

package com.example.spring.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class UserEventListener{
	
	
	@EventListener
	public void handler(UserEvent event) {
		
		System.out.println("Auditing the event "+event.getName());
	}
}

STEP4: Test

Now let’s test our changes.

I created a Test Controller class like below:

package com.example.spring.event;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
	@Autowired
	private UserService service;
	
	@PostMapping("/addUser")
	public String addUser(@RequestBody Map<String,String> request) {
		
		
		this.service.addUser(request.get("name"));
		
		return "success";
	}
}

It just calls the User Service which emits an “UserEvent”.

The listener “UserListener” listens for the event and does the auditing.

On hitting the above API through postman I got this:

Console logs:

The log which I added in “UserEventListener” got invoked as expected.

That’s it!

Asynchronous Events:

Now what if you want the events to be asynchronous?

You don’t want to block the user for auditing logic, it can happen in a parallel thread.

To do this all you have to do is use the annotation @Async on the Event listener.

In addition you need to enable asynchronous execution by adding the annotation @EnableAsync on a configuration class or to the main class:

package com.example.spring.event;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AuditListener {
	
	@Async
	@EventListener
	public void handler(AuditEvent event) {
		
		System.out.println("Auditing the event "+event.getName());
	}
}

Main class:

package com.example.spring.event;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class EventdrivenApplication {
	public static void main(String[] args) {
		SpringApplication.run(EventdrivenApplication.class, args);
	}
}

That’s it!

Now the auditing logic is executed asynchronously without blocking the main thread.

Here is the link to the entire code:

https://github.com/vijaysrj/eventdriven

Handling multiple events:

You can also use the same event listener to listen for multiple events.

To do this , just pass the event classes as parameter to the @EventListener annotation:

@EventListener(classes = UserEvent.class,OrderEvent.class)
	public void handler() {
		
		System.out.println("Auditing the event "+event.getName());
	}

Thus Spring Boot provides a simple interface to handle event driven programming.

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 )

Connecting to %s