您好,登錄后才能下訂單哦!
領域事件發布是一個領域對象為了讓其它對象知道自己已經處理完成某個操作時發出的一個通知,事件發布力求從代碼層面讓自身對象與外部對象解耦,并減少技術代碼入侵。
一、 手動發布事件
// 實體定義 @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.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異步事件監聽
四、 總結
通過對 @DomainEvents、@TransactionalEventListener的使用,在有效的解決領域事件發布的情況下,減少了對業務代碼的入侵,同時盡一步解決了數據一致性問題。
在分布式結構下,通過MQ發送事件通知給其它服務,為解決一致性問題,防止對方服務處理失敗可先將事件保久化到數據庫后,再重試。
五、 源碼
https://gitee.com/hypier/barry-jpa/tree/master/jpa-section-5
到此這篇關于詳解基于Spring Data的領域事件發布的文章就介紹到這了,更多相關Spring Data 領域事件內容請搜索億速云以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持億速云!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。