欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解基于Spring Data的領域事件發(fā)布

 更新時間:2020年04月02日 09:22:04   作者:barry的異想世界  
這篇文章主要介紹了詳解基于Spring Data的領域事件發(fā)布,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

領域事件發(fā)布是一個領域對象為了讓其它對象知道自己已經(jīng)處理完成某個操作時發(fā)出的一個通知,事件發(fā)布力求從代碼層面讓自身對象與外部對象解耦,并減少技術代碼入侵。

一、 手動發(fā)布事件

// 實體定義
@Entity
public class Department implements Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer departmentId;

  @Enumerated(EnumType.STRING)
  private State state;
}

// 事件定義
public class DepartmentEvent {
  private Department department;
  private State state;
  public DepartmentEvent(Department department) {
    this.department = department;
    state = department.getState();
  }
}

// 領域服務
@Service
public class ApplicationService {

  @Autowired
  private ApplicationEventPublisher applicationEventPublisher;

  @Autowired
  private DepartmentRepository departmentRepository;

  @Transactional(rollbackFor = Exception.class)
  public void departmentAdd(Department department) {
    departmentRepository.save(department);
    // 事件發(fā)布
    applicationEventPublisher.publishEvent(new DepartmentEvent(department));
  }
}

使用applicationEventPublisher.publishEvent在領域服務處理完成后發(fā)布領域事件,此方法需要在業(yè)務代碼中顯式發(fā)布事件,并在領域服務里引入ApplicationEventPublisher類,但對領域服務本身有一定的入侵性,但靈活性較高。

二、 自動發(fā)布事件

// 實體定義
@Entity
public class SaleOrder implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer orderId;
  
  @Enumerated(EnumType.STRING)
  private State state;

  // 返回類型定義
  @DomainEvents
  public List<Object> domainEvents(){
    return Stream.of(new SaleOrderEvent(this)).collect(Collectors.toList());
  }

  // 事件發(fā)布后callback
  @AfterDomainEventPublication
  void callback() {
    System.err.println("ok");
  }
}

// 事件定義
public class SaleOrderEvent {
  private SaleOrder saleOrder;
  private State state;
  public SaleOrderEvent(SaleOrder saleOrder) {
    this.saleOrder = saleOrder;
    state = saleOrder.getState();
  }
}

// 領域服務
@Service
public class ApplicationService {
  @Autowired
  private OrderRepository orderRepository;
  
  @Transactional(rollbackFor = Exception.class)
  public void saleOrderAdd(SaleOrder saleOrder) {
    orderRepository.save(saleOrder);
  }
}

使用@DomainEvents定義事件返回的類型,必須是一個集合,使用@AfterDomainEventPublication定義事件發(fā)布后的回調。

此方法實事件類型定義在實體中,與領域服務完全解耦,沒有入侵。系統(tǒng)會在orderRepository.save(saleOrder)后自動調用事件發(fā)布,另delete方法不會調用事件發(fā)布。

三、 事件監(jiān)聽

@Component
public class ApplicationEventProcessor {

  @EventListener(condition = "#departmentEvent.getState().toString() == 'SUCCEED'")
  public void departmentCreated(DepartmentEvent departmentEvent) {
    System.err.println("dept-event1:" + departmentEvent);
  }

  @Async
  @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, condition = "#saleOrderEvent.getState().toString() == 'SUCCEED'")
  public void saleOrderCreated(SaleOrderEvent saleOrderEvent) {
    System.err.println("sale-event succeed1:" + saleOrderEvent);
  }

  @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT, condition = "#saleOrderEvent.getState().toString() == 'SUCCEED'")
  public void saleOrderCreatedBefore(SaleOrderEvent saleOrderEvent) {
    System.err.println("sale-event succeed2:" + saleOrderEvent);
  }

  @Async
  @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
  public void saleOrderCreatedFailed(SaleOrderEvent saleOrderEvent) {
    System.out.println("sale-event failed:" + saleOrderEvent);
  }
}

1. 使用@EventListener監(jiān)聽事件

@EventListener沒有事務支持,只要事件發(fā)出就可監(jiān)控到

@Transactional(rollbackFor = Exception.class)
public void departmentAdd(Department department) {
  departmentRepository.save(department);
  applicationEventPublisher.publishEvent(new DepartmentEvent(department));
  throw new RuntimeException("failed");
}

上述情況會造成事務失敗回滾,但事件監(jiān)控端已經(jīng)執(zhí)行,可能導致數(shù)據(jù)不一致的情況發(fā)生

2. 使用@TransactionalEventListener監(jiān)聽事件

  • TransactionPhase.BEFORE_COMMIT 事務提交前
  • TransactionPhase.AFTER_COMMIT 事務提交后
  • TransactionPhase.AFTER_ROLLBACK 事務回滾后
  • TransactionPhase.AFTER_COMPLETION 事務完成后

使用TransactionPhase.AFTER_COMMIT可在事務完成后,再執(zhí)行事件監(jiān)聽方法,從而保證數(shù)據(jù)的一致性

3. TransactionPhase.AFTER_ROLLBACK回滾事務問題

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK, condition = "#departmentEvent.getState().toString() == 'SUCCEED'")
public void departmentCreatedFailed(DepartmentEvent departmentEvent) {
  System.err.println("dept-event3:" + departmentEvent);
}

由于@DomainEvents作用在實體上的,只有剛orderRepository.save(saleOrder)執(zhí)行成功后才會發(fā)送事件,故AFTER_ROLLBACK方法只會在同一事務中其它語句執(zhí)行失敗或顯式rollback時才會執(zhí)行,如果save方法執(zhí)行失敗,將不會監(jiān)聽到回滾事件。

4. @Async異步事件監(jiān)聽

  • 沒有此注解事件監(jiān)聽方法與主方法為一個事務。
  • 使用此注解將脫離原有事務,BEFORE_COMMIT也無法攔截事務提交前時刻
  • 此注解需要配合@EnableAsync一起使用

四、 總結

通過對 @DomainEvents、@TransactionalEventListener的使用,在有效的解決領域事件發(fā)布的情況下,減少了對業(yè)務代碼的入侵,同時盡一步解決了數(shù)據(jù)一致性問題。

在分布式結構下,通過MQ發(fā)送事件通知給其它服務,為解決一致性問題,防止對方服務處理失敗可先將事件保久化到數(shù)據(jù)庫后,再重試。

五、 源碼

https://gitee.com/hypier/barry-jpa/tree/master/jpa-section-5

到此這篇關于詳解基于Spring Data的領域事件發(fā)布的文章就介紹到這了,更多相關Spring Data 領域事件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • SpringCloud?客戶端Ribbon負載均衡的實現(xiàn)方法

    SpringCloud?客戶端Ribbon負載均衡的實現(xiàn)方法

    Ribbon 是 Netflix 提供的一個基于 Http 和 TCP 的客戶端負載均衡工具,且已集成在 Eureka 依賴中,這篇文章主要介紹了SpringCloud?客戶端Ribbon負載均衡的實現(xiàn)方法,需要的朋友可以參考下
    2022-06-06
  • Java非法字符: ‘\ufeff‘問題及說明

    Java非法字符: ‘\ufeff‘問題及說明

    這篇文章主要介紹了Java非法字符: ‘\ufeff‘問題及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java String 和StringBuffer的詳解及區(qū)別

    Java String 和StringBuffer的詳解及區(qū)別

    這篇文章主要介紹了Java String 和StringBuffer的詳解及區(qū)別的相關資料,需要的朋友可以參考下
    2017-05-05
  • 解析Java的Jackson庫中對象的序列化與數(shù)據(jù)泛型綁定

    解析Java的Jackson庫中對象的序列化與數(shù)據(jù)泛型綁定

    這篇文章主要介紹了解析Java的Jackson庫中對象的序列化與數(shù)據(jù)泛型綁定,Jackson通常被用來實現(xiàn)Java對象和JSON數(shù)據(jù)的相互轉換功能,需要的朋友可以參考下
    2016-01-01
  • Spring Boot中Redis數(shù)據(jù)庫的使用實例

    Spring Boot中Redis數(shù)據(jù)庫的使用實例

    Spring Boot中除了對常用的關系型數(shù)據(jù)庫提供了優(yōu)秀的自動化支持之外,對于很多NoSQL數(shù)據(jù)庫一樣提供了自動化配置的支持。本篇文章主要介紹了Spring Boot中Redis的使用實例代碼,有興趣的開業(yè)了解一下。
    2017-04-04
  • 詳解Java中List的正確的刪除方法

    詳解Java中List的正確的刪除方法

    這篇文章主要為大家詳細介紹了Java中List的正確的刪除方法,文中的示例代碼講解詳細,對我們學習有一定幫助,需要的可以參考一下
    2022-05-05
  • Java實現(xiàn)簡易生產(chǎn)者消費者模型過程解析

    Java實現(xiàn)簡易生產(chǎn)者消費者模型過程解析

    這篇文章主要介紹了Java實現(xiàn)簡易生產(chǎn)者消費者模型過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • 使用SpringBoot獲取所有接口的路由

    使用SpringBoot獲取所有接口的路由

    這篇文章主要介紹了使用SpringBoot獲取所有接口的路由方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • RabbitMQ 避免消息重復消費的方法

    RabbitMQ 避免消息重復消費的方法

    消費者端實現(xiàn)冪等性,意味著消息永遠不會消費多次,即使收到了多條一樣的消息,這篇文章給大家分享RabbitMQ 避免消息重復消費的方法,感興趣的朋友一起看看吧
    2024-03-03
  • Hibernate映射解析之關聯(lián)映射詳解

    Hibernate映射解析之關聯(lián)映射詳解

    所謂關聯(lián)映射就是將關聯(lián)關系映射到數(shù)據(jù)庫里,在對象模型中就是一個或多個引用。下面這篇文章詳細的給大家介紹了Hibernate映射解析之關聯(lián)映射的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-02-02

最新評論