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 thestart()
method on theConfigurableApplicationContext
. -
ContextStoppedEvent
Published when the
ApplicationContext
is stopped by using thestop()
method on theConfigurableApplicationContext
. -
ContextClosedEvent
Published when the
ApplicationContext
is being closed by using theclose()
method on theConfigurableApplicationContext
. -
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.
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
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,
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.
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
Filter events using Spring "SpEL"
We can filter the events only if attribute matched
@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.
@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.
@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.
@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
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:
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.