2020-3-30 다시 시작하는 스프링(6) - 이벤트퍼블리셔
AppicationEvenetPublisher
- 옵저버 패턴의 구현체. 이벤트 프로그래밍에 필요한 인터페이스 제공
- spring4.2이후로는 ApplicationEvenet를 구현할 필요가 없어졌다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
private ApplicationEventPublisher publisher;
@Override
public void run(ApplicationArguments args) throws Exception {
publisher.publishEvent(new MyEvenet(this, 100));
}
}
public class MyEvenet {
private int data;
private Object source;
public MyEvenet(Object source, int data) {
this.source = source;
this.data = data;
}
public int getData() {
return data;
}
}
@Component
public class MyEventHandler {
@EventListener
@Async
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handle(MyEvenet myEvenet) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 : " + myEvenet.getData());
}
@EventListener
@Async
public void handle(ContextRefreshedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("ConxtextRefreshedEvent");
}
@EventListener
@Async
public void handle(ContextClosedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("ConxtextClosedEvent");
}
}
@Component
public class AnotherHandler {
@EventListener
public void handle(MyEvenet myEvenet) {
System.out.println(Thread.currentThread().toString());
System.out.println("Another Handler : " + myEvenet.getData());
}
}
- 기존에는 implement ApplicationEventPublisher라는 인터페이스를 구현해야됬지만, 4.2이후로는 저렇게 해도 된다. 코드에 스프링 코드를 침투시키지 않는것이 POJO다움이다?
이벤트 발생 및 처리
- ApplicationEventPublisher.publishEvent()를 통해 발생
- ApplicationListener<이벤트>구현클래스 만들어서 빈으로 등록하기이벤트>
- 기본적으로 synchronized
- 이벤트 헨들러의 경우 @Bean으로 등록을 해야한다. 메서드에 @EventListener라는 어노테이션을 붙이면 된다. 다른 이벤트 헨들러를 만들면, 순차적으로 실행된다.
- @Order를 붙여서 이벤트헨들러에 대해 순서를 정할 수 있다.
- @Async를 붙여서 비동기적으로 처리할 수 있다. 이때는 순서보장이 안된다. 하지만 좀 더 부수적인 설정을 해줘야 다른 쓰레드에 돈다. main클래스에 @EnableAsync를 붙여야한다.
스프링이 제공하는 기본 이벤트
- ContextRefreshedEvenet: ApplicationContext를 초기화 했거나 리프래시할때 발생
- ContextStartedEvenet : ApplicationContext를 start()하여 라이프사이클 빈들이 시작신호를 받은 시점에 발생
- ContextStoppedEvenet: ApplicationContext를 stop()하여 빈들이 정지할때 신호를 받은 시점에 발생
- ContextClosedEvenet : ApplicationContext를 close()하여 싱글톤 빈들이 소멸되는 시점에 발생
- RequestHandlerEvenet: HTTP요청을 처리했을때 발생
@Component
public class MyEventHandler {
@EventListener
@Async
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handle(MyEvenet myEvenet) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 : " + myEvenet.getData());
}
@EventListener
@Async
public void handle(ContextRefreshedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("컨텍스트 리프레시 이벤트 발생 ConxtextRefreshedEvent");
System.out.println(event.toString());
}
@EventListener
@Async
public void handle(ContextStartedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("컨텍스트 시작 이벤트 ConxtextStartEvent");
System.out.println(event.toString());
}
@EventListener
@Async
public void handle(ContextClosedEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("컨텍스트 종료 이벤트 ConxtextClosedEvent");
System.out.println(event.toString());
}
}
- 이 코드를 작성하고 실행을 시켜봤다.
- 스프링실행순서
- DeomoApplication을 실행
- active profile이 있으면 설정하고 없으면 default profile설정
- AnnotationConfigServletWebServerApplicationContext 리프레시
- 톰캣시작 포트 8080매핑 => 톰캣서비스 시작 => 서브릿엔진 시작
- filter매핑(characterEncodingFilter, hiddenHttpMethodFilter, httpPutFormContenetFilter, requestContextFilter, springSecurityFilter)
- dispatcherServlet 매핑
- JPA컨테이너 빌드(Building)=> 하이버네이트 코어, 설정, 어노테이션들 설정
- Create Filter chain 여기에 필터와 인터셉트도 있다. 뭐가 먼저지?
- 컨텍스트 리프레시 이벤트 발생 !!
- 톰캣 8080에 시작 => DemoApplication 시작
- 이벤트발생
- closing AnnotationConfigServletWebServerApplicationContext
- 컨텍스트종료 이벤트
- clsoing JPA EntityManagerFactory
=> 이거를 이해해야한다.!!!
public @interface Order {
int value() default 2147483647;
}
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;
int getOrder();
}
- 이벤트헨들러의 우선순위를 결정하는 Order어노테이션은 안에 value를 전달받는다. value의 기본값은 위와같이 -21억~+21억의 범위가 있다.
Written on March 30, 2020