Spring 事件模型

簡介#

Spring 內部提供了事件機制,通過這個事件機制我們可以解耦系統的邏輯,實現松耦合的目的。 事件機制包含如下三個組件:

  • 事件發佈者: 用來發布事件,發佈事件後系統其他組件可以接受到相應的事件, 由ApplicationPublisher實現
  • 事件:可以自定義事件,如用戶創建,登錄等,可以通過繼承ApplicationEvent來實現自定義事件。
  • 事件監聽者: Spring提供了@EventListener註解,可以通過在方法上使用此註解來監聽對應的事件

示例#

  • 構造事件#

我們可以繼承ApplicationEvent來實現自己的事件,也可以直接使用ApplicationEvent 此外Spring還提供了許多其他事件:


Spring 事件模型

<code>@Data
@ToString
public class PersonLoginEvent extends ApplicationEvent {

String message;

public PersonLoginEvent(Object source, String message) {
super(source);
this.message = message;
}
}

@Data
@ToString
public class PersonCreateEvent extends ApplicationEvent {

String message;

public PersonCreateEvent(Object source, String message) {
super(source);
this.message = message;
}
}/<code>
  • 發佈事件#
  • 在系統中通過綁定ApplicationEventPublisher即可使用其發佈事件。一旦事件發佈立刻會被監聽器接收到, 發送消息是同步操作,如果希望異步發送消息可以聲明ApplicationEventMulticaster,使用Executor 來異步發送,推薦使用。

    <code>@SpringBootApplication
    public class UpscaleApplication implements CommandLineRunner,ApplicationListener<applicationstartedevent> {

    \t@Autowired
    \tApplicationEventPublisher publisher;

    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
    SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
    eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return eventMulticaster;

    \tpublic static void main(String[] args) {

    \t\tSpringApplication.run(UpscaleApplication.class, args);
    \t}

    \t@Override
    \tpublic void onApplicationEvent(ApplicationStartedEvent event) {
    log.info("Application Started!")
    \t}

    \t@Override
    \tpublic void run(String... args) throws Exception {
    \t\tlog.info("Start send message");
    \t\tpublisher.publishEvent(new PersonCreateEvent("Created Person","Successfully create person!"));
    publisher.publishEvent(new PersonLoginEvent("Person Login","Person login successfully!"));
    \t}
    }/<applicationstartedevent>/<code>
  • 監聽事件#
  • 可以通過class來限制監聽的事件對象的類型,只有匹配的事件才會被處理

    <code>import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Service;

    @Service
    @Slf4j
    public class EventListenerService {

    @EventListener(classes = {PersonCreateEvent.class})
    public void handlePersonCreate(PersonCreateEvent event){
    log.info("Peson create event:" + event);
    }

    @EventListener(classes = {PersonLoginEvent.class})
    public void handlePersonLogin(PersonLoginEvent event){
    log.info("Peson login event:" + event);
    }
    }

    ## 輸出

    2019-06-14 19:45:19.963 INFO 9456 --- [ main] i.g.j.u.s.events.EventListenerService : Peson create event:PersonCreateEvent(message=Successfully create person)
    2019-06-14 19:45:19.964 INFO 9456 --- [ main] i.g.j.u.s.events.EventListenerService : Peson login event:PersonLoginEvent(message=person login successfully)/<code>

    事務監聽器#

    如果要在數據庫操作之後發送通知消息,此時數據庫操作是在事務中執行的,事務中執行有可能成功也 有可能失敗,希望在數據庫操作成功之後接收到消息。此時我們需要TransactionalEventListener 它也是一種EventListener,只不過它和事務綁定,只有當事務完成之後才會回調此方法.

    <code>   @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handlePersonPersist(PersonCreateEvent event){

    }/<code>

    有如下四種事務階段

    • BEFORE_COMMIT: 事務提交之前
    • AFTER_COMMIT: 未指定時,默認使用
    • AFTER_ROLLBACK: 事務回滾之後
    • AFTER_COMPLETION: 事務成功提交
  • 示例#
  • 如果發送消息時,方法未使用Transaction進行標註,即使執行此方法,接收方也無法接收到消息。 此外,還需要滿足事務的階段與接收方一致

    <code>## 消息發送方
    public class PersonService {

    @Autowired
    PersonRepository repository;

    @Autowired

    EventPublisherService eventPublisherService;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Person savePerson(Person person){
    Assert.notNull(person,"Person should not be null");
    eventPublisherService.publishMessage(new PersonCreateEvent(person,"persist person"));
    return repository.save(person);
    }
    }

    ## 消息接收方

    @Service
    @Slf4j
    public class EventListenerService {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handlePersonPersist(PersonCreateEvent event){
    log.info("*********************************************");
    log.info("Peson transaction create event:" + event);
    }
    }/<code>

    輸出

    <code>2019-06-14 20:38:27.177  INFO 22835 --- [           main] i.g.j.u.s.events.EventListenerService    : *********************************************
    2019-06-14 20:38:27.177 INFO 22835 --- [ main] i.g.j.u.s.events.EventListenerService : Peson transaction create event:PersonCreateEvent(message=persist person)
    2019-06-14 20:38:27.371 INFO 22835 --- [(3)-192.168.1.7] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'/<code>

    總結#

    EventListener可以在單應用中各組件來進行消息的傳遞實現系統松耦合的目的,但是無法跨應用進行消息通知 如果需要在多個微服務之間路由消息需要藉助第三方消息中間件kafka,rabbitmq.


    分享到:


    相關文章: