Java custom events and listeners

Custom Events In Java

The Delegation Event Model is used in Java to handle events. AWT and Swing API provides many event listener and adapter classes to handle various type of events on GUI components. In this blog post, I’m writing about creating and handling non-UI and custom events in Java.

Non-UI and custom events are common in applications. We usually write the event handling code for such events in methods and mock the occurrence of an event as a method call. If the event handling code varies then we employ overloading or overriding or loose-coupling by providing many class implementations for an interface. When we want even more flexibility of enabling or disabling the execution of event handler method then we often use a boolean flag within the method to decide whether the business logic in it should be executed. Writing code to enable/disable execution within method by using multiple boolean flags when you need to enable/disable multiple events on a common source can be quite complex.

It is easy to mimic event occurrence as method calls. However, when you want the flexibility of enabling or disabling the event handling then it is easier to code the event as custom event. All events in Java are subclasses of EventObject . The custom event class should also extend this class.

An example of a custom event is the request for approval when a new workflow request is created. For writing custom event handling code these classes are required – event class, event listener, event source class, class implementing event listener or adapter class.

The event class is given in Listing 1. The event listener is given in Listing 2. The event source class is given in Listing 3. The event listener implementation class is given in Listing 4. Code to fire event is given in Listing 5 and code to instantiate and assign event listener implementation class and register the event listener is given in Listing 6.

public class ApproveRequestEvent extends EventObject < public ApproveRequestEvent(WorkflowRequest request) < super(request); >>

public interface WorkflowRequestStateChangeListener

public class WorkflowRequest < private List<WorkflowRequestStateChangeListener> listeners = new ArrayList<WorkflowRequestStateChangeListener>(); synchronized void addListener(WorkflowRequestStateChangeListener listener) < listeners.add(listener); >synchronized void removeListener(WorkflowRequestStateChangeListener listener) < listeners.remove(listener); >>

Listing 3. Event source class

public class WorkflowRequestStateChangeListenerStandardImpl implements WorkflowRequestStateChangeListener < public void handleEvent(ApproveRequestEvent event) < WorkflowRequest request = (WorkflowRequest)event.getSource(); . >>

Listing 4. Event listener implementation class

public class WorkflowFacade < private WorkflowRequestStateChangeListener listener; public WorkflowFacade(WorkflowRequestStateChangeListener listener) < this.listener = listener; >public void save(WorkflowRequest request) < //save workflow request in database . //fire event to request approval listener.handleEvent(new ApproveRequestEvent(request)); >>

Listing 5. Code that fires event

WorkflowFacade facade = new WorkflowFacade(new WorkflowStateChangeListenerStandardImpl()); WorkflowRequest request = new WorkflowRequest(); //register listener to the source request.addListener(listener); facade.save(request);

Listing 6. Code to assign event listener implementation instance and register event listener to event source

Читайте также:  Программы на java листинг

Using custom events than simply calling methods has an advantage. The code to add and remove event listener on the event source is simple. The event listener can be added or removed in the code outside the event handling code. The event listener implementation or in other words the event handling code can be changed on the fly. An off-the-shelf component can also be used to define the event handler logic if it implements appropriate event listener interface.

Writing such a code with custom events appears more logical than calling methods. However, custom events can increase the code complexity. Hence it is recommended to use them only when they are needed.

Источник

Custom Events and Generic Events in Spring

In this article, you will learn about Custom Events in Spring. Like many capabilities provided by the Spring framework, events are one of the most useful features provided by ApplicationContext in Spring. Spring allows you to publish/subscribe events synchronously as well as asynchronously.

There are several standard Events in Spring as ContextRefreshedEvent , ContextStartedEvent , ContextStoppedEvent , ContextClosedEvent , RequestHandledEvent and ServletRequestHandledEvent . Spring also allows you to create your custom event classes based on your application need. We will first learn to create a custom event and then explore the list of standard ones in another article.

Custom events can be created synchronously as well as asynchronously. The asynchronously way of creation may look complicated but provides better performance due to the non-blocking nature of execution.

A. Synchronous custom Spring Event

There are just few simple rules that needs to be followed to pub/sub events synchronously.

  • The event class should extend ApplicationEvent .
  • The publisher has to make use of ApplicationEventPublisher .
  • The event listener should implement the ApplicationListener .

1. Create the Custom event class

The custom event class has to extend the ApplicationEvent abstract class. We will create a custom UserEvent class as below.

package com.jsbd.events; import org.springframework.context.ApplicationEvent; import java.util.StringJoiner; public class UserEvent extends ApplicationEvent < //Custom property private String message; //Custom property private Integer eventId; public UserEvent(Object source) < super(source); >public UserEvent(Object source, String message, Integer eventId) < super(source); this.message = message; this.eventId = eventId; >public String getMessage() < return this.message; >public Integer getEventId() < return this.eventId; >@Override public String toString() < return new StringJoiner(", ", UserEvent.class.getSimpleName() + "[", "]") .add("message='" + message + "'") .add("source=" + source) .add("eventId wp-block-heading">2. Create an event Listener class 

We will create an EventListener by implementing the ApplicationListener interface and its method onApplicationEvent. As you can see this method accepts UserEvent as a parameter. Whenever there is an userEvent published, this method will get executed.

package com.jsbd.events; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class EventListener implements ApplicationListener  < @Override public void onApplicationEvent(UserEvent event) < System.out.println("======= UserEvent Listener ====== wp-block-heading">3. Create an event Publisher class 

The Spring framework already provides us ApplicationEventPublisher, we will autowire this using @Autowired inside our custom event publisher class.

@Component public class EventPublisher < @Autowired //Use setter based injection private ApplicationEventPublisher applicationEventPublisher; public void publishEvent(String message, Integer eventId) < applicationEventPublisher.publishEvent( new UserEvent(this, message, eventId) ); >>

4. Publish an event and test the example

Now, we will write a simple JUnit test case to publish an event and check if the listener receives it. I have also added the configuration needed to run this example.

@Configuration @ComponentScan("com.jsbd.events") public class AppConfig
@SpringJUnitConfig(AppConfig.class) class EventPublisherTest

5. Use @EventListener to subscribe events

Spring 4.2 onwards, it is possible to just annotate a method with @EventListener annotation and have the custom event as the method parameter. Spring will mark the method as a listener for the specified event UserEvent .

@Component public class AnnEventListener 

B. Asynchronous event listener using @async

Synchronous publish-subscribe is blocking in nature, can have bad impact on application performance. To get rid of this, Spring provides asynchronous event listener using @async annotation.

The first step is to enable the async processing by annotating the configuration with @EnableAsync as below.

@Configuration @ComponentScan("com.jsbd.events") @EnableAsync public class AppConfig < //other configs if needed >

Then, the second step is to use @async with the listener as shown below

@Component public class AnnEventListener < //Async Event listener @EventListener @Async public void asyncListener(UserEvent userEvent) < System.out.println("===== Async UserEvent Listener ==== EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme data-enlighter-highlight data-enlighter-linenumbers data-enlighter-lineoffset data-enlighter-title data-enlighter-group>@Bean @Scope("singleton") ApplicationEventMulticaster eventMulticaster() < SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); //optionally set the ErrorHandler eventMulticaster.setErrorHandler(TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER); return eventMulticaster; >

C. Runtime event Filtering using SpEL

Occasionally, you may want to filter out the events delivered to the listener. Spring allows you to define a SpEL based expression based on which the matched values are delivered to the event listener. Only the UserEvents with the eventId ==120 are delivered to this listener.

@Component public class AnnEventListener < //Event Listener with a Spring SpEL expression @EventListener(condition = "#userEvent.eventId == 102") public void processEvent(UserEvent userEvent) 

D. Ordering EventListeners using @Order

You can use the @Order annotation when you want a specific eventListener to get invoked before another one. Remember, the annotation with the least value has the highest execution priority. So in the below example, listenerA gets executed before listenerB.

@Component public class AnnEventListener < @EventListener @Order(1) public void listenerA(UserEvent userEvent) < System.out.println("\n===== UserEvent ListenerA ====="); System.out.println(userEvent); >@EventListener @Order(5) public void listenerB(UserEvent userEvent) 

E. Generic events

Spring also allows you to use a generic event class to publish any type of event. As shown in the below code, instead of creating too many classes for each event types, you can just create a GenericEvent class with more properties to send any type of event.

public class GenericEvent implements ResolvableTypeProvider < private T message; private Object source; public GenericEvent(Object source, T message) < this.source = source; this.message = message; >@Override public ResolvableType getResolvableType() < return ResolvableType.forClassWithGenerics( getClass(), ResolvableType.forInstance(getSource()) ); >public T getMessage() < return message; >public Object getSource() < return source; >@Override public String toString() < return new StringJoiner(", ", GenericEvent.class.getSimpleName() + "[", "]") .add("message=" + message) .add("source EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme data-enlighter-highlight data-enlighter-linenumbers data-enlighter-lineoffset data-enlighter-title data-enlighter-group>@Component public class GenericEventListener < @EventListener public void listenEvent(GenericEvent event) < System.out.println(event); >>

Now, let us publish few events and test.

@SpringJUnitConfig(AppConfig.class) public class GenericEventTest < @Autowired private ApplicationEventPublisher eventPublisher; @Test public void publishEvent() < GenericEvent event1 = new GenericEvent<>(this, new Person("John")); GenericEvent event2 = new GenericEvent<>(this, "Hello"); eventPublisher.publishEvent(event1); eventPublisher.publishEvent(event2); > >
public class Person < private String name; public Person(String name) < this.name = name; >public String getName() < return name; >public void setName(String name) < this.name = name; >@Override public String toString() < return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]") .add("name='" + name + "'") .toString(); >>
  • Spring’s eventing mechanism is designed for simple communication between Spring beans within the same application context.
  • However, for more sophisticated needs you can explore the Spring Integration project.

Spring events provide a great way to communicate between Spring beans. Please share your thoughts on this article in the comments below. You can download the complete example from the link below.

By Bikram Kundu | 2020-04-11T15:34:40+00:00 April 11th, 2020 | Categories: Spring Framework | Tags: Spring 5.x, Spring Events |

Источник

Java: Creating a custom event

Here's an example of how create your own events and listen to them. It's called the observer pattern. In this example the initiator prints "Hello" and the HelloListener responds with "Hello there!".

// An interface to be implemented by everyone // interested in "Hello" events interface HelloListener < void someoneSaidHello(); > 
// Someone who says "Hello" class Initiater < private List listeners = new ArrayList(); public void addListener(HelloListener toAdd) < listeners.add(toAdd); >public void sayHello() < System.out.println("Hello!"); // Notify everybody that may be interested. for (HelloListener hl : listeners) hl.someoneSaidHello(); > > 
// Someone interested in "Hello" events class Responder implements HelloListener < @Override public void someoneSaidHello() < System.out.println("Hello there!"); > > 

And here's a little example runner:

class Test < public static void main(String[] args) < Initiater initiater = new Initiater(); Responder responder = new Responder(); initiater.addListener(responder); initiater.sayHello(); // "Hello!" and "Hello there!" > > 

Comments (2)

Than you for this example. It works perfectly!

Источник

Оцените статью