스프링 데이터 JPA-11.도메인 이벤트(스프링 데이터 Common)
스프링 데이터 Common: 도메인 이벤트
도메인 관련 이벤트를 발생시키기
스프링 프레임워크의 이벤트 관련 기능
- https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#context-functionality-events
- ApplicationContext extends ApplicationEventPublisher
- 이벤트: extends ApplicationEvent
- 리스너
- implements ApplicationListener
- @EventListener
- implements ApplicationListener
스프링 데이터의 도메인 이벤트 Publisher
- @DomainEvents
- 현재 캡처된 모든 도메인 이벤트 (도메인 이벤트를 전부 저장)
- @AfterDomainEventPublication
- 현재 보유한 모든 도메인 이벤트를 지웁 (쌓여있던 이벤트 다 보낸 다음, 컬렉션에 담아져 있던 걸 비워야 한다.(메모리 누수))
- extends AbstractAggregateRoot
- 현재는 save() 할 때만 발생
AbstractAggregateRoot 들어가서 소스 확인해 보면.. 둘 다 구현되어 있는 걸 볼 수 있다.
public class AbstractAggregateRoot<A extends AbstractAggregateRoot<A>> {
private transient final @Transient List<Object> domainEvents = new ArrayList<>();
...
/**
* Clears all domain events currently held. Usually invoked by the infrastructure in place in Spring Data
* repositories.
*/
@AfterDomainEventPublication
protected void clearDomainEvents() {
this.domainEvents.clear();
}
/**
* All domain events currently captured by the aggregate.
*/
@DomainEvents
protected Collection<Object> domainEvents() {
return Collections.unmodifiableList(domainEvents);
}
...
구현
public class PostPublishedEvent extends ApplicationEvent {
private final Post post;
public PostPublishedEvent(Object source){
super(source);
this.post = (Post) source;
}
public Post getPost(){
return post;
}
}
스프링은 2가지 방법으로 Listener 제공
// 1. public class PostListener implements ApplicationListener<PostPublishedEvent> {
// 2.
public class PostListener{
// 1. @Override
// 2.
@EventListener
public void onApplicationEvent(PostPublishedEvent event) {
System.out.println("================== start");
System.out.println(event.getPost().getTitle() + "is published!!!");
System.out.println("================== emd");
}
}
AbstractAggregateRoot 구현
@Entity @Getter @Setter
public class Post extends AbstractAggregateRoot<Post> {
@Id @GeneratedValue
private Long id;
private String title;
@Lob //255자 넘는 경우
private String content;
@Temporal(TemporalType.TIMESTAMP)
private Date created;
public Post publish() {
this.registerEvent(new PostPublishedEvent(this));
return this;
}
}
테스트
@Configuration
public class PostRepositoryTestConfig {
@Bean
public PostListener postListener(){
return new PostListener();
}
// PostListner 바로 구현 (PostListener 클래스 대신 사용 가능)
/*
@Bean
public ApplicationListener<PostPublishedEvent> postListner(){
return event -> {
System.out.println("================ start ================");
System.out.println(event.getPost().getTitle() + "is published !!@@##$$");
System.out.println("================ end ================");
};
}
*/
}
@ExtendWith(SpringExtension.class)
@DataJpaTest // 슬라이싱 테스트라서 데이터,JPA관련된 빈만 등록된다. (ex.@Component 빈으로 등록 안됨, @Repository는 빈으로 등록 된다.)
@Import(PostRepositoryTestConfig.class)// 테스트용 추가설정 import
class PostRepositoryTest {
@Autowired
PostRepository postRepository;
@Autowired
ApplicationContext applicationContext; // 애플리케이션 컨텍스트는 빈팩토리 그 이상. 단순 IoC 컨테이너 역할은 빈팩토리가 이미 다 한다.
// AbstractAggregateRoot 미구현시
@Test
public void event(){
Post post = new Post();
post.setTitle("event");
PostPublishedEvent event = new PostPublishedEvent(post);
applicationContext.publishEvent(event);
}
@Test
public void crud(){
Post post = new Post();
post.setTitle("hibernate");
assertFalse(postRepository.contains(post));
postRepository.save(post.publish()); // AbstractAggregateRoot 구현 registerEvent
assertTrue(postRepository.contains(post));
}
}