ApplicationContext는 Bean Factory의 역할 + Spring의 여러가지 기능들을 담당하고 있다. 이 ApplicationContext는 다음과 같은 인터페이스들을 상속받고 있는데
- ApplicationEventPublisher
- EnvironmentCapable
- HierarchicalBeanFactory
- ListableBeanFactory
- MessageSource
- ResourceLoader
- ResourcePatternResolver
이들 중 가장 먼저 살펴볼 것은, EnvionmentCapable 인터페이스이다.
이 Environment가 제공하는 기능은 크게 프로파일과 프로퍼티로 나눌 수 있는데, 먼저 프로파일에 대해 알아보자.
1. 프로파일 인터페이스
이 프로파일은 개발/ 운영서버가 분리된 환경이거나 할 때 특정 상황에서만 빈을 등록하거나 조작할 수 있는 기능을 제공해준다.
//test 환경에서만 사용할 Repository
@Repository
@Scope("test")
public class BookRepository {
}
// test 환경에서만 등록되어야 하는 Configuration file
@Configuration
@Profile("test")
public class TestConfiguration{
@Bean
public BookRepository bookRepository(){
return new TestBookRepository();
}
}
2. 프로퍼티 인터페이스
: 이는 프로퍼티나 시스템 환경변수 등에 접근할 수 있는 기능을 제공해 준다. 프로퍼티를 사용하여 접근할 수 있는 대상의 범위와 우선순위는 다음과 같다.
- ServletConfig 매개변수
- ServletContext 매개변수
- JNDI
- JVM 시스템 프로퍼티
- JVM 시스템 환경변수
// Application.class에 properties 파일 명시
@SpringBootApplication
@PropertySource("classpath:/app.properties")
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class,args);
}
}
// AppRunner.class에서 프로퍼티 파일 내 변수 접근
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception{
Environment environment = ctx.getEnvironment();
String appName = environment.getProperty("app.name");
String appAbout = environment.getProperty("app.about");
}
}
3. MessageSource 인터페이스
message.properties에 등록된 메세지들을 읽어오는 인터페이스이다.
먼저 resources 아래에 messages.properties, messages_ko_KR.properties들을 생성해주면
- resources
- Resource Bundle 'messages'
-messages.properties
-messages_ko_KR.properties
이런 구조로 프로퍼티 파일이 생성된다.
자바에서의 사용법은 다음과 같다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
MessageSource messageSource; // #1. ApplicationContext가 상속받는 MessageSource 객체를 주입받는다.
@Override
public void run(ApplicationArguments args) throws Exception{
//#2. properties 파일에 접근
System.out.println(messageSource.getMessage("greeting", new String[]{"gayoung"}, Locale.ROOT));
System.out.println(messageSource.getMessage("greeting", new String[]{"gayoung"}, Locale.KOREA)); // #3. messages_ko_RK.properties파일에 접근
}
}
4. ApplicationEventPublisher 이벤트
이 기능은 이벤트를 발생시키고, 발생시킨 이벤트를 핸들링 하는 기능을 제공한다.
먼저 스프링 4.2 이전에 방식을 알아보자. 이 방식은 EventClass에서 ApplicationEvent 인터페이스를 상속받고,
Bean으로 등록된 EventHandler에서 ApplicationListener<이벤트클래스>를 상속받아야 한다.
// #1. event class
public class MyEvent extends ApplicationEvent {
private int data;
public MyEvent(Object source) {
super(source);
}
public MyEvent(Object source, int data){
super(source);
this.data = data;
}
public int getData(){
return data;
}
}
// #2. eventHandler class : 빈으로 등록되어야 함.
@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("이벤트 받았다."+ event.getData() );
System.out.println("이벤트 받았다."+ event.getSource() );
System.out.println("이벤트 받았다."+ event.getTimestamp());
System.out.println("이벤트 받았다."+ event.getClass());
}
}
# 3. 이벤트 발생부
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher eventPublisher;
@Override
public void run(ApplicationArguments args) throws Exception {
eventPublisher.publishEvent(new MyEvent(this,100));
}
}
스프링 4.2 이상에서는 이벤트와 리스너에서 어떠한 인터페이스도 상속받지 않아도 된다.
리스너에서 @EventListener 어노테이션만 붙여준다면~
// #1. event Class
public class MyEvent { //#1.1 implements 부분이 사라짐
private int data;
private Object source;
public MyEvent(Object source, int data){
this.source = source;
this.data = data;
}
public int getData(){
return data;
}
public Object getSource() {
return source;
}
}
// #2. EventHandler class
@Component
public class MyEventHandler {
@EventListener // #2.1 Bean등록과 EventListner 등록은 필수. 돼지꼬리 땡땡.
public void onApplicationEvent(MyEvent event) {
System.out.println("이벤트 받았다."+ event.getData() );
System.out.println("이벤트 받았다."+ event.getSource() );
//System.out.println("이벤트 받았다."+ event.getTimestamp()); // #2.2 얘는 에러남
System.out.println("이벤트 받았다."+ event.getClass());
}
}
// #3. 이벤트 발생부
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher eventPublisher;
@Override
public void run(ApplicationArguments args) throws Exception {
eventPublisher.publishEvent(new MyEvent(this,100));
}
}
또한 만약 동일 이벤트에 대한 핸들러가 여러개일 경우, 이 핸들러간의 순서를 정하거나 / 아니면 비동기적으로 처리할 수도 있다.
순서를 정하고 싶으면 @Order(Ordered.HIGHEST_PRECEDENCE) 옵션을 사용하면 되고,
비동기적으로 처리하고 싶으면 @Async 애노테이션 및 Application.class에서 @EnableAsync 애노테이션을 사용하면 된다.
// #1. 새로운 핸들러 추가
@Component
public class MyNewEventHandler {
@EventListener
//@Order(Ordered.HIGHEST_PRECEDENCE)
@Async
public void newHandle(MyEvent event){
System.out.println(Thread.currentThread().toString());
System.out.println("새로운 핸들러 호출");
}
}
// #2. 기존의 핸들러
@Component
public class MyEventHandler {
@EventListener
//@Order(Ordered.HIGHEST_PRECEDENCE+2)
@Async
public void onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다."+ event.getData() );
}
}
# 3. 이벤트 호출부
@SpringBootApplication
@EnableAsync // #3.1 비동기 처리를 위한 애노테이션 추가
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
또한 스프링에서 기본적으로 제공하는 이벤트를 처리할 수도 있다.
@Component
public class MyEventHandler {
@EventListener
@Async
public void handle(ContextRefreshedEvent event){
System.out.println("context is refreshed");
}
}
위의 예시는 ApplicationContext가 초기화 될 때 발생하는 ContextRefreshedEvent를 이벤트 핸들러에서 처리한 것이다. 그 외에도 스프링에서 기본적으로 제공하는 이벤트는 아래와 같은 것들이 있다.
- ContextRefreshedEvent : ApplicationContext를 초기화 했거나 리프레시 했을 때 발생
- ContextStartedEvent : ApplicationContext를 start()하여 라이프 사이클 빈들이 시작 신호를 받은 시점에 발생
- ContextStoppedEvent: ApplicationContext를 stop()하여 라이프 사이클 빈들이 정지 신호를 받은 시점에 발생
- ContextClosedEvent : ApplicationContext를 close()하여 싱글톤 빈들이 소멸되는 시점에 발생
- RequestHandledEvent: HTTP요청을 처리했을 때 발생
5. ResourceLoader 인터페이스
이 인터페이스는 다른 파일들을 읽어와야 할 때 사용할 수 있는 기능을 제공한다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ResourceLoader resourceLoader; //#1. resourceLoader 주입
@Override
public void run(ApplicationArguments args) throws Exception {
Resource resource = resourceLoader.getResource("classpath:test.txt"); // #2. 가져올 resource의 경로 입력
System.out.println("resource exits?" + resource.exists());
System.out.println("resource : " + resource);
}
}
'Study > Spring' 카테고리의 다른 글
스프링 핵심기술 06. AOP (0) | 2022.02.13 |
---|---|
스프링 핵심기술 05. 추상화 (0) | 2022.02.13 |
스프링 핵심기술 03. 스프링 IoC 컨테이너 - Bean의 Scope (0) | 2022.02.03 |
스프링 핵심기술 02. 스프링 IoC 컨테이너 - @Autowire (0) | 2022.02.02 |
스프링 핵심기술 01. 스프링 IoC 컨테이너 - ApplicationContext와 Bean 설정 방법 (0) | 2022.02.02 |