Spring Event
- 특정 이벤트를 발행(publish) 하면, 해당 이벤트가 발행되기를 대기하고 있던 리스너가 실행된다.
- 이벤트 발행 클래스와 리스너 클래스간 코드 의존성이 없다.
- 리스너 실행을 비동기로 실행할 수 있다.
이벤트 클래스
public class CustomEvent extends ApplicationEvent {
@Getter
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
}
- ApplicationEvent 를 상속받는다.
- super 생성자를 반드시 실행해 줘야 한다.
이벤트 발행자(publisher) 클래스
@RequiredArgsConstructor
@Component
public class CustomEventPublisher {
private final ApplicationEventPublisher publisher;
public void publish(final String message) {
System.out.println("publish custom event");
CustomEvent event = new CustomEvent(this, message);
publisher.publishEvent(event);
}
}
- 스프링의 publisher 클래스인 ApplicationEventPublisher 클래스를 이용해 이벤트를 발행한다.
이벤트 리스너 클래스
@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("Received custom event" + event.getMessage());
}
}
//동일한 기능을 어노테이션 기반으로 작성
@Component
public class CustomEventListener {
@EventListener
public void handleCustomEvent(CustomEvent event) {
System.out.println("Received custom event" + event.getMessage());
}
}
ApplicationListener 상속 방법
- ApplicationListener 를 구현하여 작성한다.
- 제네릭 타입에 받아들일 이벤트 타입을 넣는다.
- onApplicationEvent 메소드를 오버라이드 하여 이벤트 발행시 받아들일 메소드를 작성한다.
어노테이션 기반 방법
- @EventListener 어노테이션 하나만으로 작동 되므로 훨씬 간편하다.
Async 설정 추가
@Configuration
public class AsyncEventConfig {
@Bean
public ApplicationEventMulticaster simpleApplicationMultiCaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return multicaster;
}
}
//어노테이션 기반
@EnableAsync
@SpringBootApplication
public class Application {
}
@Component
public class CustomEventListener {
@EventListener
@Async
public void handleCustomEvent(CustomEvent event) {
System.out.println("Received custom event" + event.getMessage());
}
}
Configuration 설정 기반
- 추가적으로 ApplicationEventMulticaster 빈을 생성해주며, async executor 를 추가해준다.
어노테이션 기반
- async사용 설정을 위해 스프링부트 루트 클래스에 @EnableAsync 를 선언해준다.
- 실행될 이벤트 리스너 메서드에 @Async를 적용해준다.
@DomainEvent 를 이용해 도메인에서 이벤트 발행
도메인 클래스
@Entity
public class CustomDomain {
@Id
@GeneratedValue
private Long id;
private int age;
@Transient
private final Collection<CustomEvent> customEvents = new ArrayList<>();
@DomainEvents
public Collection<CustomEvent> events() {
return customEvents;
}
@AfterDomainEventPublication
public void afterEventPublication() {
customEvents.clear();
}
public void addAge() {
this.age++;
customEvents.add(new CustomEvent(this, "user age is " + age));
}
}
- 도메인의 addAge를 실행하는 경우 발행될 이벤트 목록에 하나의 이벤트를 추가한다.
- @DomainEvents 어노테이션을 통해 이벤트가 담겨져 있는 컬렉션을 리턴한다.
- @DomainEvents 가 달려있는 메소드는 Repository.save로 해당 도메인이 저장되는 때에 메소드가 실행되고, 리턴된 이벤트 목록이 스프링에 의해 발행된다.
- @AfterDomainEventPublication 메소드는 repository.save() 실행시 등록 이벤트가 없더라도 실행된다.
Repository
@Repository
public interface CustomRepository extends JpaRepository<CustomDomain, Long> {
}
사용
public void test() {
CustomDomain domain = customRepository.findById(1L)
.orElseThrow(() -> new NullPointerException());
domain.addAge();
customRepository.save(domain);
}
AbstractAggregateRoot 를 이용해 이벤트 발생
@Entity
public class CustomDomain extends AbstractAggregateRoot<CustomDomain> {
@Id
@GeneratedValue
private Long id;
private int age;
@AfterDomainEventPublication
public void afterEventPublication() {
System.out.println("publish finish!@@@@@");
}
public void addAge() {
this.age++;
registerEvent(new CustomEvent(this, "user age is " + age));
}
}
- AbstractAggregateRoot를 상속 받고, registerEvent 로 이벤트를 등록 한다.
- @AfterDomainEventPublication 메소드는 repository.save() 실행시 등록 이벤트가 없더라도 실행된다.
- repository.save() 를 실행 해야 등록된 이벤트가 실행된다 (@Transactional 에 의한 변경감지로 저장은 실행되지 않음.)
'SPRING' 카테고리의 다른 글
Spring Batch FaultTolerance (장애 허용) (0) | 2021.12.13 |
---|---|
Spring Envers 로 자동 이력 관리 (0) | 2021.06.20 |
Spring 메소드 실행 실패시 재실행 (0) | 2021.05.24 |
Spring Rest Docs 사용법 (0) | 2021.05.02 |
Spring Batch (0) | 2021.04.28 |