스프링 데이터 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

스프링 데이터의 도메인 이벤트 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));
    }
}





© 2019. by jaeuk