Skip to content

Lab M02P01

Using events, you can decouple application components. Spring framework's core functionality supports events by implementation of the Observer pattern. Spring itself fires several types of events as Context events. You can implement your own eventing model and use Spring's API to fire events and to listen for events.

  1. Let's implement application event which notify other components of your application about newly created Book entity. Events typically inform about something already happened. First, create the BookCreatedEvent class in the ite.librarymaster.event package. Event structure can look like:

      public class BookCreatedEvent {
        public Long id;
        public String catId;
        public String title;
        public String publisher;
        public String author;
        public String isbn;
        public BookGenre genre;
     }
    
    Event is just the data holder, so we can use event properties public access, or we can use Java record.

  2. Now, implement an event publisher. Define interface with the fireEvent() method and then implementation. The best is to introduce generic event support in your application. Create the GenericEvent class, which holds generic event payload.

    public class GenericEvent <T> {
        private T payload;
        public GenericEvent(T payload) {
            this.payload = payload;
        }
        public T getPayload() {
            return payload;
        }
    }
    
    Then define publisher interface.

    public interface DomainEventPublisher {
        void fireEvent(GenericEvent payload);
    }
    
    And publisher implementation. Spring framework provides the ApplicationEventPublisher bean, you can use to publish events.

    @Component
    public class SpringDomainEventPublisher implements DomainEventPublisher {
       @Autowired
       ApplicationEventPublisher applicationEventPublisher;
    
       @Override
       public void fireEvent(GenericEvent payload) {
           applicationEventPublisher.publishEvent(payload);
       }
    }
    
  3. Now, use the DomainEventPublisher in the LibraryAdminServiceImpl to fire the BookCreatedEvent when new Book entity is created. We can say the LibraryAdminService is the domain service, so it can produce domain events like the BookCreatedEvent.

  4. What is still missing, is an event listener. You can create one in the service package for instance. Listener will just log received event. Implement Spring bean LoggerAuditService with method annotated with the EventListener annotation.

      @EventListener
      public void log(GenericEvent event) {
          LOG.info("AUDIT LOG: {}", event.getPayload());
      }
    

    Now, run LibraryAdminServiceImplTest integration test and check logs for event messages.

    It would be great, if the BookCreatedEvent is fired only if current Transaction is successfully COMMITTED. Spring supports binding events to Tx. Just replace the @EventListener annotation with the @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) one. Event processing is executed in a caller's Thread by default, but you can add @Async annotation at Listener's method to execute event processing asynchronously. You need to add @EnableAsync annotation to SpringBoot configuration. (just check the Application class)

  5. The example of the Spring context event listener is in the ContextEventListener class. If you need to execute custom logic upon Spring context initialization phases, the Spring Context based events is the right ones to use.

Application events

The event publishing is provided by ApplicationContext. Spring’s eventing mechanism is designed for simple communication between Spring beans within the same application context. It is implementation of the Observer pattern. Spring handles either custom event classes which extends the ApplicationEvent, or any arbitrary classes.

Build-in events

Spring framework is able to publish build-in events as:

  • ContextRefreshedEvent
  • ContextStartedEvent
  • ContextStoppedEvent
  • ContextClosedEvent
  • RequestHandledEvent
  • ServletRequestHandledEvent

Tx bound events

The @TransactionalEventListener annotation, which is an extension of @EventListener, that allows binding the listener of an event to a phase of the transaction.

  • TransactionPhase.AFTER_COMMIT
  • TransactionPhase.AFTER_ROLLBACK
  • TransactionPhase.AFTER_COMPLETION
  • TransactionPhase.BEFORE_COMMIT