Events in spring

java

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.

1 Aug 2017 #java #spring #basics #howto