Spring Ch 1. IoC 컨테이너(4) - MessageSource와 EventPublisher


MessageSource

  • 한국어 페이지 뿐 아니라 다른 언어를 사용하는 페이지를 출력하고자 할 때 언어만 다른 html파일을 사용한다면 유지보수가 어렵고 비효율적일 것이다.
  • SpringBoot에서는 국제화 기능을 제공하는 인터페이스 MessageSource를 @Autowired로 자동 등록할 수 있다.
  • .properties파일에 key-value 형식으로 값을 저장하고, MessageSource 객체로 메시지를 받아 사용한다.
1
2
# messages_en.properties - 영어권 메시지, {0}은 String으로 치환되어진다.
greeting = Hello, {0}
1
2
# messages.properties - 디폴트 메시지, 요청되는 언어에 해당하는 properties파일이 존재하지 않는다면 해당 파일의 키값이 출력된다.
greeting = 안녕, {0}
1
2
3
4
5
6
7
8
9
10
11
@Component
public class AppRunner implements ApplicationRunner{
@Autowired
MessageSource messageSource;
@Override
public void run(ApplicationArguments args) throws Exception{
//getMessage는 (key,arguments,Locale)을 인자로 하여 메시지를 출력한다.
System.out.println(messageSource.getMessage("greeting", new String[] {"Heang"}, Locale.ENGLISH));
System.out.println(messageSource.getMessage("greeting", new String[] {"Heang"}, Locale.getDefault() ));
}
}

MessageSource 리로딩

  • MessageSource빈은 ResourceBundleMessageSource 객체를 싱글톤으로 주입받기 때문에 호출마다 갱신되지 않는다.
1
2
3
4
5
6
7
8
9
10
11
@Autowired
MessageSource messageSource;
@Override
public void run(ApplicationArguments args) throws Exception{
//반복 중 메시지가 변경되어도 적용되지 않는다.
while(true) {
System.out.println(messageSource.getMessage("greeting", new String[] {"Heang"}, Locale.ENGLISH));
System.out.println(messageSource.getMessage("greeting", new String[] {"Heang"}, Locale.getDefault() ));
Thread.sleep(3000);
}
}
  • ReloadableReourceBundleMessageSource는 애플리케이션 실행 중 리소스를 reload하는 기능을 가지고 있어 MessageSource객체를 해당 객체로 등록한다면 이를 해결할 수 있다.
1
2
3
4
5
6
7
8
9
10
@Bean
public MessageSource messageSource() {
var messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages");
//UTF-8로 디폴트 인코딩을 설정해주어야 한글 깨짐이 발생하지 않는다.
messageSource.setDefaultEncoding("UTF-8");
//5초마다 메시지를 reload
messageSource.setCacheSeconds(5);
return messageSource;
}

EventPublisher

  • Spring에서는 ApplicationEventPublisher가 이벤트 프로그래밍에 필요한 인터페이스를 제공하고 있다.
  • ApplicationEventPublisher는 publish 메소드를 통해 이벤트를 발생시키며 ApplicationListener가 이벤트 리스너의 역할을 수행한다.
1
2
3
4
5
6
7
8
9
10
   @Autowired
//ApplicationEventPublisher는 ApplicationContext 인터페이스에 상속되어있다.
ApplicationContext appCtx;
//ApplicationEventPublisher pub;
@Override
public void run(ApplicationArguments args) throws Exception{
//publishEvent 메소드를 통해 이벤트를 발생시킨다.
appCtx.publishEvent(new MyEvent(this,100));
//pub.publishEvent(new MyEvent(this,100));
}
  • 이벤트 클래스는 ApplicationEvent를 상속받아 등록할 수 있다.
  • 그러나 Spring 4.2버전부터는 ApplicationEvent를 상속받지 않아도 이벤트로 사용할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class MyEvent //extends ApplicationEvent 
    {
    private int data;
    private Object source;
    public MyEvent(Object source, int data) {
    //super(source);
    this.source = source;
    this.data = data;
    }
    public Object getSource() {
    return source;
    }
    public int getData() {
    return data;
    }
    }
  • 이벤트 핸들러는 ApplicationListener 인터페이스를 사용하여 구현한다.

  • 이벤트 클래스와 마찬가지로 인터페이스를 사용하여 구현하지 않아도 @EventListener 어노테이션으로 이벤트 처리를 구현할 수 있다.
  • @Order 어노테이션은 이벤트의 우선순위를 설정한다. LOWEST_PRECEDENCE, HIGHTEST_PRECEDENCE을 사용하여 우선순위를 정할 수 있고 Integer 값으로 직접 지정할 수도 있다.
  • @Async 어노테이션은 이벤트를 비동기로 실행하도록 설정한다. 이때 비동기적으로 발생하는 이벤트들은 각 스레드에서 발생하고 스레드 스케줄에 의해 순서가 바뀔 수 있으므로 @Order 어노테이션의 의미는 옅어진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
public class MyEvenetHandler //implements ApplicationListener<MyEvent>
{
//@Override
// public void onApplicationEvent(MyEvent event) {
// System.out.println("이벤트 "+event.getData());
// }

@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE+2)//HIGHEST_PRECEDENCE를 MIN_VALUE로 값이 낮은 이벤트를 먼저 실행시킨다.
@Async //@Async 어노테이션으로 비동기적 이벤트를 설정할 수 있음.
public void onApplicationEvent(MyEvent event) {
System.out.println("이벤트 "+event.getData());
}
}
@Component
public class AnotherHandler {
@EventListener
@Order(Ordered.LOWEST_PRECEDENCE)
@Async
public void handle(MyEvent myEvent) {
System.out.println("Another Event "+myEvent.getData());
}
}

스프링에서 제공하는 기본 이벤트

  • ApplicationEventPublisher가 발생시키는 기본 이벤트는 다음과 같다.
  1. ContextRefreshedEvent: ApplicationContext를 초기화/refresh 했을 때 발생
  2. ContextStartedEvent: ApplicationContext객체가 start()를 호출하여 라이프사이클 빈들이 시작 신호를 받은 시점에 발생
  3. ContextStoppedEvent: ApplicationContext객체가 stop()를 호출하여 라이프 사이클 빈들이 정지 신호를 받은 시점에 발생
  4. ContextClosedEvent: ApplicationContext객체가 close()를 호출하여 싱글톤 빈들이 소멸되는 시점에 발생
  5. RequestHandledEvent: HTTP 요청을 처리했을 때 발생

댓글