springframework/Spring Data JPA

Spring Data Common : Domain Event

Jungsoomin :) 2020. 11. 17. 15:03

도메인 이벤트

  • Entity 의 변화를 이벤트로 간주
  • ApplicationContext 의 EventPublisher 의 기능을 이용하여 EventListener 로 받아줌
  • 이벤트 기반 프로그래밍 에 사용

이벤트 작성 방법.(Spring Frame Work ApplicationContext 의 EventPublisher 에 기술 되어 있음)

  • ApplicationEvent 구현클래스 작성
public class PostPublishedEvent extends ApplicationEvent {

    private final Post post;

    public PostPublishedEvent(Object source) {
        super(source);
        this.post = (Post)source;
    }

    public Post getPost() {
        return post;
    }
}

이벤트 리스너 작성.(Spring Frame Work ApplicationContext 의 EventPublisher 에 기술되어 있음)

  • ApplicationListener<T> 구현 클래스 작성
  • @EventListener 메서드를 가진 클래스 작성
@Component
public class PostListener implements ApplicationListener<PostPublishedEvent> {
    @Override
    public void onApplicationEvent(PostPublishedEvent postPublishedEvent) {
        System.out.println("=======================");
        System.out.println(postPublishedEvent.getPost()+ " is published!");
        System.out.println("=======================");
    }
}
@Component
public class AnnotationPostListener {

    @EventListener
    public void onApplicationEvent(PostPublishedEvent event){
        System.out.println("=======================");
        System.out.println(event.getPost()+ " is published!");
        System.out.println("=======================");
    }
}

이벤트 퍼블리케이션(Spring Data , 이외는 ApplicationContext 의 EventPublisher 에 기술되어 있음)

  • Spring Data 의 경우 Entity 클래스에 AbstractAggregateRoot<E> 상속 후 registerEvent() 를 사용하는 메서드 기술
  • 이외는 ApplicationContext에서 EventPublisher 를 가져와 이벤트 퍼블리싱
@Entity
public class Post extends AbstractAggregateRoot<Post> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @Lob
    private String content;

    @Temporal(TemporalType.TIMESTAMP)
    private Date created;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }
	
    // 
    public Post publish() {
        this.registerEvent(new PostPublishedEvent(this));
        return this;
    }
}

슬라이싱 테스트 진행

@RunWith(SpringRunner.class)
@DataJpaTest
@Import(PostListener.class)
public class DomainEventTest {

    @Autowired
    private PostRepository postRepository;

    @Autowired
    private ApplicationContext applicationContext;

//    @Test
    public void testEventPublisher(){
        Post post = new Post();
        post.setTitle("Event");
        PostPublishedEvent publishedEvent = new PostPublishedEvent(post);
        applicationContext.publishEvent(publishedEvent);
    }

    @Test
    public void testSpringDataEventPublication(){
        Post post = new Post();
        post.setTitle("Domain Event");

        postRepository.save(post.publish());
    }
}
=======================
me.soomin.jpa.post.Post@79ecc507 is published!
=======================

AbstractAggregateRoot<E> 와 @DomainEvent, @AfterDomainEventPublication

Spring Datasave 될때 이벤트를 자동으로 퍼블리싱해준다.

  • Entity 에 쌓여있던 Event 를 save() 하는 순간 전부 퍼블리싱 해준다.
  • 이벤트를 모아 저장하는 메서드@DomainEvent 를 가진 메서드이다.
  • 메모리 누수 방지를 위해 컬렉션을 비워주는 메서드@AfterDomainEventPublication 을 가진 메서드이다.
public class AbstractAggregateRoot<A extends AbstractAggregateRoot<A>> {
    @Transient
    private final transient List<Object> domainEvents = new ArrayList();

    public AbstractAggregateRoot() {
    }

    protected <T> T registerEvent(T event) {
        Assert.notNull(event, "Domain event must not be null!");
        this.domainEvents.add(event);
        return event;
    }

    @AfterDomainEventPublication
    protected void clearDomainEvents() {
        this.domainEvents.clear();
    }

    @DomainEvents
    protected Collection<Object> domainEvents() {
        return Collections.unmodifiableList(this.domainEvents);
    }
...