发布领域事件
通过repositories
管理的实体是聚合根。在领域驱动设计(DDD)的应用中,这些聚合根通常会发布领域事件。Spring Data提供了一个名为@DomainEvents
的注解,可以在聚合根的一个方法上使用该注解,就可以非常方便的发布领域事件了。如下所示:
@Entity
class Person {
private Long id;
private String name;
private int age;
@Transient
private final transient List<PersonEvent> domainEvents = new ArrayList();
/**
* 使用 @DomainEvents 的方法可以返回一个单一的事件实例或一个事件的集合。
* 它必须不接受任何参数。
*/
@DomainEvents
Collection<PersonEvent> domainEvents() {
return domainEvents;
}
/**
* 在所有的事件都被发布后,可以使用@AfterDomainEventPublication注释的方法
* 清理要发布的事件列表(除其他用途外)。
*/
@AfterDomainEventPublication
void callbackMethod() {
domainEvents.clear();
}
public void saveDomainEvent(){
domainEvents.add(new PersonSaveEvent(this));
}
public void deleteDomainEvent(){
domainEvents.add(new PersonDeleteEvent(this));
}
}
每次调用Spring Data Repository 的save(...)
、saveAll(...)
、delete(...)
或deleteAll(...)
方法时都会调用这些方法,也就是增/删/改都会触发领域事件。
监听领域事件
使用注解 @TransactionalEventListener
来监听领域事件,如下所示为监听PersonSaveEvent
事件
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void savePersonEvent(PersonSaveEvent saveEvent){
this.doSomething(saveEvent.getPerson());
}
遇到的问题
DomainEvents
方法不能使用Arrays.asList
返回集合,会报错。
出错原因:
Arrays.asList
返回的集合是 Arrays
的内部类 ArrayList
,查看源码发现ArrayList
本身是没有实现remove
方法的,所以会调用其父类 AbstractList
的 remove
方法。
而 AbstractList
其实也没有实现 remove
方法,而是直接抛出个异常,也就是上面坑里抛出的错误。
- 直接在
@DomainEvents
标注的方法内创建指定类型事件。导致删除时也会调用saveEvent
逻辑。
Spring官方文档明确标注有-> 每次调用Spring Data Repository 的save(...)
、saveAll(...)
、delete(...)
或deleteAll(...)
方法时都会触发领域事件。所以在domainEvents
方法中根本就不能明确当前触发的到底是什么事件(创建 or 删除?)。
如需源码,请私信或留言。