亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

詳解基于Spring Data的領域事件發布

發布時間:2020-09-07 17:56:02 來源:腳本之家 閱讀:361 作者:barry的異想世界 欄目:編程語言

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

一、 手動發布事件

// 實體定義
@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);
    // 事件發布
    applicationEventPublisher.publishEvent(new DepartmentEvent(department));
  }
}

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

二、 自動發布事件

// 實體定義
@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());
  }

  // 事件發布后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定義事件發布后的回調。

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

三、 事件監聽

@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監聽事件

@EventListener沒有事務支持,只要事件發出就可監控到

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

上述情況會造成事務失敗回滾,但事件監控端已經執行,可能導致數據不一致的情況發生

2. 使用@TransactionalEventListener監聽事件

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

使用TransactionPhase.AFTER_COMMIT可在事務完成后,再執行事件監聽方法,從而保證數據的一致性

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)執行成功后才會發送事件,故AFTER_ROLLBACK方法只會在同一事務中其它語句執行失敗或顯式rollback時才會執行,如果save方法執行失敗,將不會監聽到回滾事件。

4. @Async異步事件監聽

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

四、 總結

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

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

五、 源碼

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

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

岱山县| 邹城市| 衡南县| 康保县| 鲜城| 青海省| 会东县| 恩平市| 肇东市| 白河县| 绍兴县| 南开区| 新乡市| 高雄市| 邓州市| 孟州市| 永兴县| 大姚县| 湾仔区| 扎鲁特旗| 当阳市| 青神县| 类乌齐县| 柯坪县| 玉树县| 大化| 宁明县| 惠东县| 建德市| 沽源县| 曲靖市| 福建省| 运城市| 通海县| 宜兴市| 庆城县| 巨野县| 会宁县| 祁门县| 临沧市| 民丰县|