Events in spring
With spring 4.2 (released more than one year ago) serious improvements regarding embedded events were made. You probably already know it, but I’ve never had a chance to properly investigate it. Lately, when digging into code base of the system I’m currently working on I got an opportunity to see it in action and after quick glimpse, I decided to investigate it a bit further.
To dispatch an event all you need to do is to inject ApplicationEventPublisher and call publishEvent
@RequiredArgsConstructor
@Service
public class LabelService {
private final ApplicationEventPublisher eventPublisher;
public Category createCategory(String name) {
final Category category = new Category(name);
eventPublisher.publishEvent(new LabelCreated<>(category));
return category;
}
}
To handle event just annotate method with @EventListener and add expected event object as method param.
@Slf4j
@Service
public class MobilePushNotificationService {
@EventListener
public void handleUserCreatedAction(UserCreatedEvent event) {
log.info("Mobile notification: User created {}", event.getUser());
}
}
Spring will be able to select proper listener when handling generic events (note ResolvableTypeProvider which allows to properly resolve generics in the runtime by the framework):
@Getter
@RequiredArgsConstructor
public class LabelCreated<T extends Label> implements ResolvableTypeProvider {
private final T label;
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), label.getClass());
}
}
@Slf4j
@Service
class LabelNotificationService {
@EventListener
public void onCategoryCreation(LabelCreated<Category> event) {
log.info("Category created {}. Sending for acceptance", event.getLabel());
}
}
You can filter and react on events using SpEL. To apply SpEL use condition attribute. What’s more EventListener can be even bound to current transaction. To handle the event inside the transaction use @TransactionalEventListener.
@Slf4j
@Service
class PublishedArticleNotifier {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onArticleCreated(ArticleCreatedEvent event) {
log.info("Publishing article {} to facebook", event.getArticle());
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void articleSaveFailure(ArticleCreatedEvent event) {
log.info("Publishing article {} save failure to backup storage", event.getArticle());
}
@EventListener(condition = "#event.updateType == T(com.pchudzik.blog.example.springevents.article.Article.UpdateType).MAJOR")
public void onArticleUpdate(ArticleUpdateEvent event) {
log.info("Sending notification to the author about major article update {}", event.getArticle());
}
}
Event handling code can be executed asynchronously. Annotate method or class with @Async and remember to enable asynchronous support and you are good to go.
@Slf4j
@Service
@Async
class EmailNotificationService {
@EventListener
public void handleUserCreatedAction(UserCreatedEvent event) {
log.info("Email: User created {}", event.getUser());
}
}
That’s all for now. As you can see dispatching events and handling them in your application is really easy with spring framework. What’s more, it is possible and very easy to take advantage of advanced spring features like transaction management and asynchronous events handling.
All code samples can be found on my GitHub.
If you've enjoyed or found this post useful you might also like: