-
Notifications
You must be signed in to change notification settings - Fork 0
ApplicationEventPublisher 사용하기
Jeonghwa Heo edited this page Jun 4, 2023
·
2 revisions
스프링 4.2이전엔 ApplicationEvent
를 상속받아야한다.
- 빈으로 등록되는게 아닌 원하는 이벤트를 담아 전송하는 클래스다.
- 원하는 데이터가 있다면 실어서 보낼 수 도 있다.
package com.smart.user.domain;
import org.springframework.context.ApplicationEvent;
public class MailEvent extends ApplicationEvent {
private String data;
public MailEvent(Object source) {
super(source);
}
public MailEvent(Object source, String data) {
super(source);
this.data = data;
}
public String getData() {
return data;
}
}
ApplicationContext 가 발생시키는 방법을 가지고 있다.
ApplicationContext extends ApplicationEventPublisher
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
}
AppRunner 클래스 생성
-
ApplicationRunner
구현 -
ApplicationContext
를 주입 받아도되고ApplicationEventPublisher
를 주입받는 것도 가능함.
package com.smart.mail.domain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationEventPublisher.publishEvent(new MailEvent(this, "data"));
}
}
- 이벤트 핸들러 생성 → 빈으로 등록이 되어야함
- 스프링 4.2이전엔
ApplicationListener
가 되어야함.
package com.smart.mail.domain;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MailEventHandler implements ApplicationListener<MailEvent> {
@Override
public void onApplicationEvent(MailEvent event) {
System.out.println("이벤트를 받았다. "+event.getData()+"에게 메일을 전송하자.");
}
}
스프링 컨테이너를 띄우면 이벤트가 발생한다.
- 어떠한 스프링코드도 들어가있지 않은 이벤트 클래스
package com.smart.mail.domain;
public class MailEvent {
private String data;
private Object source; // 이벤트 소스를 받고싶다면 추가
public MailEvent(Object source, String data) {
this.source = source;
this.data = data;
}
public Object getSource() {
return source;
}
public String getData() {
return data;
}
}
- 애노테이션만 들어가있는 이벤트 핸들러
package com.smart.mail.domain;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MailEventHandler{
@EventListener // 이벤트를 처리하는 애노테이션. 메서드 이름 마음대로 해도됨.
public void eventHandle(MailEvent event) {
System.out.println("이벤트를 받았다. "+event.getData()+"에게 메일을 전송하자.");
}
}
@Order
애노테이션을 사용하여 이벤트 우선순위를 설정함
Thread[main,5,main]
이벤트를 받았다. data에게 메일을 전송하자.
Thread[main,5,main]
다른 이벤트를 받았다. data에게 메일을 전송하자.
package com.smart.mail.domain;
import static java.lang.Thread.currentThread;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
public class AnotherMailEventHandler {
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
public void eventHandle(MailEvent event) {
System.out.println(currentThread().toString());
System.out.println("다른 이벤트를 받았다. "+event.getData()+"에게 메일을 전송하자.");
}
}
Thread[main,5,main]
이벤트를 받았다. data에게 메일을 전송하자.
Thread[main,5,main]
다른 이벤트를 받았다. data에게 메일을 전송하자.
@Async 애노테이션을 사용한다.
- 어떤 스레드가 실행시킬지 모르기 때문에 순서는 당연히 보장이 되지 않는다.
package com.smart.mail.domain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
@Component
@EnableAsync
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationEventPublisher.publishEvent(new MailEvent(this, "data"));
}
}
@EnableAsync : 여러스레드를 만들어 이벤트를 발생시킴.
package com.smart.mail.domain;
import static java.lang.Thread.currentThread;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class MailEventHandler{
@EventListener
@Async
public void eventHandle(MailEvent event) {
System.out.println(currentThread().toString());
System.out.println("이벤트를 받았다. "+event.getData()+"에게 메일을 전송하자.");
}
}
package com.smart.mail.domain;
import static java.lang.Thread.currentThread;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AnotherMailEventHandler {
@EventListener
@Async
public void eventHandle(MailEvent event) {
System.out.println(currentThread().toString());
System.out.println("다른 이벤트를 받았다. "+event.getData()+"에게 메일을 전송하자.");
}
}
Thread[task-1,5,main]
다른 이벤트를 받았다. data에게 메일을 전송하자.
Thread[task-2,5,main]
이벤트를 받았다. data에게 메일을 전송하자.
- 회원가입시 User를 테이블에 저장하는 트랜잭션과 메일발송에 대한 트랜잭션 및 스레드를 분리함으로써 회원가입 버튼을 누른 사용자는 메일발송이 끝날 때 까지 응답을 기다리지 않아도 된다.
@Service
public class UserService {
private final ApplicationEventPublisher eventPublisher;
...
@Transactional
public Long join(UserSaveDto saveDto) {
checkDuplicateEmail(saveDto.getEmail());
checkDuplicateNickname(saveDto.getNickname());
User user = saveDto.toEntity();
String authCode = getAuthCode();
userRepository.save(user);
eventPublisher.publishEvent(new MailAuthEvent(user.getEmail(), authCode)); // 이벤트 호출
return user.getUserId();
}
...
}
@Component
public class MailEventHandler{
private final MailService mailService;
public MailEventHandler(MailService mailService) {
this.mailService = mailService;
}
// 회원가입 트랜잭션과 분리하기 위해 Transaction을 새로 만들어줌.
@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener
// 성능을 위해 비동기로 처리한다(회원가입 스레드와 메일전송 스레드 분리)
@Async
public void eventHandle(MailAuthEvent event) {
mailService.sendAuthMail(event.getEmail(), event.getAuthCode());
}
}