public class CancelOrderService {
private final RefundService refundService;
@Transactional
public void cancel(OrderNo orderNo) {
// ...
refundService.refund(order.getPaymentId());
// ...
}
}
ShippingInfoChangedEvent
이벤트를 사용하면 서로 다른 도메인 로직이 섞이는 것을 방지할 수 있다.
public class Order {
public void cancel() {
verifyNotYetShipped();
this.state = OrderState.CANCELED;
Events.raise(new OrderCanceldEvent(number.getNumber()));
}
}
이벤트를 사용하면 기능 확장도 용이하다.
ApplicationEventPublisher
를 이용ApplicationEventPublisher
를 사용public class OrderCancedEvent {
private String orderNumber;
// 생성자, getter ...
}
OrderCancedEvent
ApplicationEventPublisher
를 사용하면 된다.
applicationEventPublisher.publishEvent(…)
도메인 엔티티 객체 내부에서 이벤트를 발생시키도록 구현할 수도 있다.
public class Order {
public void cancel() {
verifyNotYetShipped();
this.state = OrderState.CANCELED;
Events.raise(new OrderCanceldEvent(number.getNumber()));
}
}
Events
클래스는 ApplicationEventPublisher
를 사용해 이벤트를 발생시키도록 구현한 것이다. import org.springframework.context.ApplicationEventPublisher;
public class Events {
private static ApplicationEventPublisher publisher;
static void setPublisher(ApplicationEventPublisher publisher) {
Events.publisher = publisher;
}
public static void raise(Object event) {
if (publisher != null) {
publisher.publisherEvent(event);
}
}
}
Events
클래스의 초기화는 아래와 같이 구현한다. @Configuration
public class EventsConfig {
// ApplicationContext는 ApplicationEventPublisher를 상속하고 있다.
@AutoWired
private ApplicationContext applicationContext;
@Bean
public InitializingBean eventsInitializer() {
return () -> Events.setPublisher(applicationContext);
}
}
InitializingBean
인터페이스는 스프링 빈 객체를 초기화할 때 사용하는 인터페이스다.이벤트 처리는 스프링이 제공하는 @EventListener
애너테이션을 사용해 구현한다.
@EventListener(OrderCancedEvent.class)
public void handle(OrderCanceldEvent event) {
refundService.refund(event.getOrderNumber());
}
Events.raise()
를 이용해 이벤트 발생시킴Events.raise()
는 ApplicationEventPublisher
를 이용해 이벤트 출판ApplicationEventPublisher
는 @EventListener
메서드를 찾아 실행
@Async
애너테이션을 사용하면 이벤트 핸들러가 별도 스레드로 실행된다.
@EnableAsync
애너테이션으로 비동기 기능을 활성화 후 이벤트 핸들러 메서드에 @Async
애너테이션을 붙인다.
@SpringbootApplication
@EnableAsync
public class ShopApplication {
public static void main(String[] args) {
SpringApplication.run(ShopApplication.class, args);
}
}
@Async
@EventListener(OrderCanceldEvent.class)
public void handle(OrderCanceledEvent event) {
// ...
}
이벤트를 사용할 때 동기로 하든 비동기로 하든 이벤트 처리 실패와 트랜잭션 실패를 함께 고려해야 한다.
@TransactionalEventListener
를 사용하면 트랜잭션 커밋 여부에 따라 핸들러 실행 여부를 결정할 수 있다.