Skip to content

Spring event

From Spring Application Context introduction, we know one of the capability that ApplicationContext has, but `BeanFactory1 doesn't have is event publication.

ApplicationContext provides the event handling through the ApplicationEvent class and the ApplicationListener interface.

If a bean that implements the ApplicationListener interface is deployed into the context, every time an ApplicationEvent gets published to the ApplicationContext, that bean is notified. Essentially, this is the standard Observer design pattern.

Standard and Custom Events

  • ContextRefreshedEvent

    Published when the ApplicationContext is initialized or refreshed.

  • ContextStartedEvent

    Published when the ApplicationContext is started by using the start() method on the ConfigurableApplicationContext.

  • ContextStoppedEvent

    Published when the ApplicationContext is stopped by using the stop() method on the ConfigurableApplicationContext.

  • ContextClosedEvent

    Published when the ApplicationContext is being closed by using the close() method on the ConfigurableApplicationContext.

  • RequestHandledEvent

    Published when an HTTP request has been serviced.

  • ServletRequestHandledEvent

    A subclass of RequestHandledEvent that adds Servlet-specific context information.

  • Custom Events

To publish a custom ApplicationEvent, a bean needs to get the ApplicationEventPublisher, and call its publishEvent method.

Java
public class UserCreatedEvent extends ApplicationEvent {
    private final String userId;

    public UserCreatedEvent(User user) {
        this.userId = user.getId();
    }

    // other methods...
}

public class UserService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public User createUser(UserCreationDTO userDTO) {
        // create user 
        User user = saveUserInRepo();
        publisher.publishEvent(new UserCreatedEvent(user));
        return user;
    }
}

public class UserCreatedNotifier implements ApplicationListener<UserCreatedEvent> {
    public void onApplicationEvent(UserCreatedEvent event) {
        // handle the event
    }
}

Annotation-based Event Listeners

Since Spring 4.2, Spring start to support annotation based events model, which is a significantly improvements,

  • an object doesn't necessarily inherit from ApplicationEvent
  • the receiver can be an annotated method in a bean instead of inherit from ApplicationListener

We can annotated the event receiver method using @EventListener.

Generic Annotation-based event listener

Java
public class UserCreatedEvent extends ApplicationEvent {
    private final long userId;
    private String region;

    public UserCreatedEvent(User user) {
        this.userId = user.getId();
        this.region = user.getRegion();
    }

    // other methods...
}

public class UserService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public User createUser(UserCreationDTO userDTO) {
        // create user 
        User user = saveUserInRepo();
        publisher.publishEvent(new UserCreatedEvent(user));
        return user;
    }
}

public class UserCreatedNotifier {
    @EventListener
    public void onApplicationEvent(UserCreatedEvent event) {
        // handle the event
    }
}

For transaction phase, we can use a specific event listener @TransactionalEventListener. For example,

Java
public class UserService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public User createUser(UserCreationDTO userDTO) {
        // create user 
        User user = saveUserInRepo();
        publisher.publishEvent(user);
        return user;
    }
}

public class UserCreatedNotifier {
    @TransactionalEventListener
    public void onUserCreated(User event) {
        // handle the event
    }
}

Listen multiple events

We can listen multiple events for no parameter.

Java
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    // ...
}

Filter events using Spring "SpEL"

We can filter the events only if attribute matched

Java
@EventListener(condition = "#event.region == 'Europe'")
public void handleUserCreated(UserCreatedEvent event) {
    // ...
}

Publish a new Event as the result

We can return a new event as a return value.

Info

Not supported for async listeners.

Java
@EventListener
public UpdateUserEvent handleUserCreated(UserCreatedEvent event) {
    // ...
    return new UpdateUserEvent(...);
}

Asynchronous Listeners

If you want a particular listener to process events asynchronously, you can reuse the regular @Async support.

Java
@EventListener(condition = "#event.region == 'Europe'")
@Async
public void handleUserCreated(UserCreatedEvent event) {
    // ...
}

Ordering Listeners

If you need one listener to be invoked before another one, you can add the @Order annotation to the method declaration.

Java
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
    // notify appropriate parties via notificationAddress...
}

Generic Events

You can also use generics to further define the structure of your event. Consider using an EntityCreatedEvent where T is the type of the actual entity that got created. For example, you can create the following listener definition to receive only EntityCreatedEvent for a User:

Java
@EventListener
public void onUserCreated(EntityCreatedEvent<User> event) {
    // ...
}

Due to type erasure, this works only if the event that is fired resolves the generic parameters on which the event listener filters (that is, something like class UserCreatedEvent extends EntityCreatedEvent<User> { …​ }).

In certain circumstances, this may become quite tedious if all events follow the same structure (as should be the case for the event in the preceding example). In such a case, you can implement ResolvableTypeProvider to guide the framework beyond what the runtime environment provides. The following event shows how to do so:

Java
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
    }
}

Info

This works not only for ApplicationEvent but any arbitrary object that you send as an event.