스프링 이벤트 2 - 이벤트 기능 적용

아래 예제는 github에 소스를 올려놓았습니다.


이제 본격적으로 스프링의 이벤트 기능을 적용해보자.

이벤트 기능을 적용하기 위해서는 이벤트를 생성하고 발급하는 publisher가 필요하고 발급된 이벤트를 리스닝하는 event listener를 설정해야한다.

  1. 주문 이벤트 생성

주문 이벤트는 단순하게 주문(Order)만을 가진 VO 객체로 만들자.

1
2
3
4
5
6
7
8
9
10
11
public class OrderedEvent {
private final Order order;

public OrderedEvent(final Order order) {
this.order = order;
}

public Order getOrder() {
return order;
}
}

코드와 같이 이벤트 객체는 아무 상속도 받지 않는 POJO를 사용할 수 있다.
그리고 이벤트 클래스의 네이밍은 일반적으로 현재를 기점으로 이전에 발생한 일이기 때문에 과거형으로 명명한다고한다.

  1. 주문 이벤트 publish

이제 OrderedEvent를 발급해보자.
이벤트를 발급하기 위해서 ApplicationEventPublisher가 필요하다.
스프링 부트에서 기본적으로 bean으로 등록해놓았기 때문에 해당 bean을 주입받으면 된다.

그리고 이제 우리는 Notifier component와 결합도를 낮추기 위해 직접적으로 사용하지 않을 것이다.
그렇기 때문에 EmailSender, KakaoTalkSender를 제거한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Service
public class OrderService {
// private final EmailSender emailSender;
// private final KakaoTalkSender kakaoTalkSender;

private final ApplicationEventPublisher eventPublisher;

public OrderService(final ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}

public Order order(final String user, final OrderRequest orderRequest) {
Order order = doOrder(user, orderRequest);

if (order.isFailed()) {
throw new IllegalStateException("Order failed");
}

// emailSender.sendNotification(order);
// kakaoTalkSender.sendNotification(order);

eventPublisher.publishEvent(new OrderedEvent(order)); // 이벤트 발급

return order;
}

private Order doOrder(final String user, final OrderRequest orderRequest) {
String product = orderRequest.getProduct();
int price = orderRequest.getPrice();

if (user.isEmpty()) {
return Order.fail(user, product, price);
}

return Order.success(user, product, price);
}
}

위와 같이 ApplicationEventPublisher.publishEvent를 호출하여 이벤트를 발급한다.

사실 이전에 publishEvent 메서드는 ApplicationEvent를 상속받은 이벤트 객체만을 발급할 수 있었다.
하지만 스프링 4.2 버전부터 모든 객체(Object)를 이벤트로서 발급할 수 있게됐다.
아래는 ApplicationEventPublisher 소스이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@FunctionalInterface
public interface ApplicationEventPublisher {

/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an application event. Events may be framework events
* (such as RequestHandledEvent) or application-specific events.
* @param event the event to publish
* @see org.springframework.web.context.support.RequestHandledEvent
*/
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}

/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an event.
* <p>If the specified {@code event} is not an {@link ApplicationEvent},
* it is wrapped in a {@link PayloadApplicationEvent}.
* @param event the event to publish
* @since 4.2
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);

}
  1. 주문 이벤트 listening

이제 이벤트 퍼블리셔를 통해 발급한 주문 이벤트를 리스닝하는 EventListner가 필요하다.
이벤트를 리스닝하는 것은 간단하다.
리스닝 하고자하는 이벤트를 인자로 갖는 메서드를 만들고 메서드에 @EventListener를 달면 된다.

기존에 OrderNotifier의 구현체를 이벤트 리스너로 사용하기 위해 sendNotification 메서드의 인자를 Order에서 OrderedEvent로 변경하자.

1
2
3
public interface OrderNotifier {
void sendNotification(final OrderedEvent orderedEvent);
}

그리고 OrderNotifier 구현체인 EmailSenderKakaoTalkSender에 이벤트 리스닝 기능을 위해 메서드에 @EventListener를 달아주자.

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class EmailSender implements OrderNotifier {

@Override
@EventListener // 이벤트 리스닝 기능 활성화
public void sendNotification(final OrderedEvent orderedEvent) {
Order order = orderedEvent.getOrder();

System.out.printf("send email to %s - order (%s: %d)%n",
order.getOrderer(), order.getProduct(), order.getPrice());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class KakaoTalkSender implements OrderNotifier {

@Override
@EventListener // 이벤트 리스닝 기능 활성화
public void sendNotification(final OrderedEvent orderedEvent) {
Order order = orderedEvent.getOrder();

System.out.printf("send kakaotalk to %s - order (%s: %d)%n",
order.getOrderer(), order.getProduct(), order.getPrice());
}
}

IDE로 IntelliJ IDEA를 사용한다면 gutter icon으로 이벤트 퍼블리셔와 리스너를 확인할 수 있다.

  • 이벤트 퍼블리셔

  • 이벤트 리스너

그리고 gutter icon을 클릭하면 이벤트 퍼블리셔, 리스너로 바로 이동할 수 있다.

(community도 해당 기능을 제공하는지는 잘 모르겠다..)


여기까지 전부 수행했다면 기본적인 이벤트 처리기능 적용은 끝난 것이다.
이벤트 처리 기능을 확인하기 위해 main method를 호출해보자.

기존과 같은 동작을 하는 것을 확인할 수 있다.


이처럼 직접적으로 component를 의존하지 않아도 해당 기능을 사용함으로서 component들간의 결합도를 낮출 수 있다.

다음에는 스프링의 이벤트 처리 기능에 대한 추가적인 기능에 대해 알아보겠다.

Share