0. 사용법
- 회원가입을 하고나서 이메일로 가입을 축하합니다 라는 메시지로 보낸다고 가정
- 그럴려면 이메일이 필요한데 event를 통해 이메일만 따로 보내서 로직을 작성
- 의존관계를 가지지 않아도 된다는 장점
1. 개념
- 이벤트: 프로그램이 감지할 수 있는 특정 사건 또는 행동
- 이벤트 소스: 이벤트를 발생시키는 객체 또는 컴포넌트
- 이벤트 리스너: 이벤트가 발생했을 때 실행될 동작을 정의하는 객체
- js처럼 어떤 이벤트가 발생시킬지 정하는것이다.
- 이벤트 프로그래밍은 GUI 애플리케이션, 게임개발, 실시간 시스템 등에서 널리 사용
- 시스템이 사용자나 외부환경의 변화에 즉시 반응할 수 있도록 해준다
1). 구성요소
- 이벤트 프로듀서: 이벤트를 생성하고 발행하는 컴포넌트
- 이벤트 컨슈머: 이벤트를 구독하고 처리하는 컴포넌트
- 이벤트 버스: 이벤트를 전달하고 분배하는 매커니즘
2). 장점
- 확장성: 시스템 구성 요소간의 느슨한 결합으로 확장성이 높아짐
- 유연성: 다양한 이벤트 소스와 이벤트 리스너를 쉽게 추가하고 제거할 수 있다
- 반응성: 이벤트가 발생하면 즉시 반응할 수 있어 실시간 처리에 유리함
- 유지보수성: 코드의 모듈화가 용이하여 유지보수가 편리함
3). Java와 관계
- AWT(Abstract Window Toolkit)와 Swing 라이브러리를 통해 이벤트 프로그래밍을 제공
- Event Object: 이벤트 정보를 담고 있는 객체
- Event Listener Interface: 이벤트를 처리하기 위해 구현해야 하는 인터페이스
- Event Source Object: 이벤트를 발생시키는 객체
4). Spring Boot와 관계
- Event Source: 이벤트를 발생시키는 컴포넌트, 주로 비즈니스 로직내에서 특정 조건이 충족될 때 이벤트 생성
- Event Listener: 이벤트가 발생했을 때 이를 처리하는 컴포넌트로 특정 이벤트 유형을 감지하고 필요한 작업을 수행
- Event Publisher: 이벤트를 생성하고 리스너에게 전달하는 역할
(0). ApplicationListner
- 스프링이 실행될때 실행하고 싶은게 있으면 여기서 구현해도 된다
@Component
@Slf4j
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public boolean supportsAsyncExecution() {
return ApplicationListener.super.supportsAsyncExecution();
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
log.info("애플리케이션 시작");
}
}
@Component
@Slf4j
public class ShutdownApplicationListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("애플리케이션 종료");
}
}
(1). ApplicationEvent
- Spring에서 제공하는 기본 이벤트 클래스
- 이벤트 발생 시간을 기록하며, 이를 통해 이벤트 발생 시점을 추적할 수 있다
public class CustomEvent extends ApplicationEvent {
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
(2). ApplicationEventPublisher
- 이벤트를 게시(publish)하는 역할, 이벤트를 생성하고 이를 모든 리스너에게 전달
@Service
public class CustomEventPublisher {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void publishEvent(String message) {
CustomEvent customEvent = new CustomEvent(this, message);
eventPublisher.publishEvent(customEvent);
}
}
(3). EventListener
@Component
public class CustomEventListener {
@EventListener
public void handleCustomEvent(CustomEvent event) {
System.out.println("event.getMessage() = " + event.getMessage());
}
}
@SpringBootApplication
public class EventApplication implements CommandLineRunner {
@Autowired
private CustomEventPublisher customEventPublisher;
public static void main(String[] args) {
SpringApplication.run(EventApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
customEventPublisher.publishEvent("hello, world");
}
}
2. 비동기 이벤트 처리
@Async를 통해 비동기 이벤트 처리
- 시간이 오래걸리는 작업, 외부 시스템과의 통신을 비동기로 처리함으로써 유연하게 처리가능
@Configuration
@EnableAsync
public class AsyncConfig {
}
1). Listener
@Component
public class CustomEventListener {
@EventListener
@Async
public void handleCustomEvent(CustomEvent event) throws InterruptedException {
System.out.println("event.getMessage() = " + event.getMessage());
try{
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("event.getMessage() = " + event.getMessage());
}
}
2). @Async 쓰레드풀 지정
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "customTaskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("CustomTaskExecutor-");
executor.initialize();
return executor;
}
}
1). Listener
@Component
public class CustomEventListener {
@EventListener
@Async("customTaskExecutor")
public void handleCustomEvent(CustomEvent event) throws InterruptedException {
System.out.println("event.getMessage() = " + event.getMessage());
System.out.println("event.getMessage() = " + Thread.currentThread().getName());
Thread.sleep(5000);
}
}
3. 트랜잭션 이벤트 처리
- 이벤트를 데이터 일관성을 보장하면서 처리할 수 있도록 해준다
- 아이템명과 가격만 있다고 생각
- 트랜잭션 이벤트 처리는 DB작업과 이벤트 처리가 원자적으로 어루어져 신뢰성과 일관성을 높일 수 있다
1). Event Source
@Getter
public class ItemRegistrationEvent extends ApplicationEvent {
private final String name;
private final double price;
public ItemRegistrationEvent(Object source, String name, double price) {
super(source);
this.name = name;
this.price = price;
}
}
2). Event Listener
@Component
public class ItemRegistrationEventListener {
@Async("customTaskExecutor")
@TransactionalEventListener
public void handleItemRegistrationEvent(ItemRegistrationEvent event) {
System.out.println("Handing item registration event for item: " + event.getName() + ", price: " + event.getPrice());
}
}
3). Item Service 계층
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
private final ApplicationEventPublisher applicationEventPublisher;
@Transactional
public void registerItem(String name, double price) {
Item item = new Item(name, price);
itemRepository.save(item);
System.out.println("Registering item = " + name);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
applicationEventPublisher
.publishEvent(new ItemRegistrationEvent(this, name, price));
}
});
}
}
4. 스프링 4.2 이후버전
- ApplicationEvent 상속안받아도 된다
- 그냥 객체만들어서 사용해도 가능
- Publisher는 안만들고 그냥 전달해두고 용도니까 따로 클래스 안만들수있으면 안만들자
@Getter
public class MyEvent {
private int data;
private Object source;
public MyEvent(int data, Object source) {
this.data = data;
this.source = source;
}
}
@Component
public class MyEventHandler {
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
@Order(Ordered.LOWEST_PRECEDENCE)
@Order(1)
public void eventHandlerMethod(MyEvent myEvent) {
System.out.println("myEvent.getData() = " + myEvent.getData());
}
}
@SpringBootApplication
public class EventApplication implements CommandLineRunner {
@Autowired
private ItemService itemService;
@Autowired
ApplicationEventPublisher applicationEventPublisher;
public static void main(String[] args) {
SpringApplication.run(EventApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
applicationEventPublisher.publishEvent(new MyEvent(100, this));
}
}
1). 스프링에서 제공하는 기본 이벤트
@EventListener
public void eventRefreshedHandleMethod(ContextRefreshedEvent event) {
System.out.println("초기화 및 리프레쉬 : " +event.getApplicationContext().toString());
}
@EventListener
public void eventCloseHandleMethod(ContextClosedEvent event) {
System.out.println("컨텍스트 close : " +event.getApplicationContext().toString());
}
@EventListener
public void eventStartHandleMethod(ContextStartedEvent event) {
System.out.println("컨텍스트 start : "+event.getApplicationContext().toString());
}
@EventListener
public void eventHandleMethod(ContextStartedEvent event) {
System.out.println("컨텍스트 stop : " +event.getApplicationContext().toString());
}
@EventListener
public void eventRequestHandleMethod(RequestHandledEvent event) {
System.out.println("처리완료");
}