Spring Core (2) IoC - 2
4. Bean의 Scope
스프링 빈은 기본적으로 싱글톤 스코프를 가진다. 싱글톤 빈은 IoC 컨테이너 내에서 유일한 객체로, 의존성 주입 시 항상 동일한 객체를 사용한다. 따라서 만약 싱글톤 객체 내부에 필드가 있다면 해당 필드의 값이 공유된다(멀티 스레드 환경에서 주의!!).
이와 반대되는 개념으로 프로토타입 스코프가 있다. 프로토 타입 스코프로는 Request, Session, WebSocket 등이 있다.
[Single.java]
@Component
public class Single {
@Autowired
private Proto proto;
public Proto getProto() {
return proto;
}
}
[Proto.java]
@Scope(value = "prototype")
@Component
public class Proto {
}
싱글톤 빈 안에서 프로토타입 빈을 참조할 경우 의존성 주입에 의해 최초 한 번만 이루어지기 때문에 프로토타입 빈을 사용하는 의미가 없어진다. 따라서 아래와 같은 기법들을 사용하게 된다.
A. proxyMode 옵션
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Proto {
}
이렇게 옵션을 주게 되면 CGLIB이라는 서드파티 라이브러리를 사용하여 클래스 기반의 프록시 인스턴스를 만들어 빈으로 등록하게 된다. proxyMode
의 default 값은 ScopedProxyMode.DEFAULT
이다.
B. Object-Provider 사용(스프링 의존 O)
@Component
public class Single {
@Autowired
private ObjectProvider<Proto> proto;
public Proto getProto() {
return proto.getIfAvailable();
}
}
C. Provider(스프링 의존 X)
@Component
public class Single {
@Autowired
private Provider<Proto> proto;
public Proto getProto() {
return proto.get();
}
}
5. ApplicationContext 구현체
ApplicationContext는 여러 인터페이스를 상속받는다. 다음은 ApplicationContext 구현체가 상속받아 구현하는 인터페이스들이다.
A. Environment
Environment는 프로파일과 프로퍼티를 다루는 인터페이스이다. ApplicationContext는 EnvironmentCapable을 상속받는데, 이를 통해 getEnvironment를 호출하여 Environment 인터페이스를 불러올 수 있다.
i) Profile
클래스나 메소드 앞에 @Profile("myProfile1")
형태로 적용할 수 있다. 해당 어노테이션을 적용 할 경우 myProfile1 이라는 프로파일이 지정되어야만 스프링 빈이 생성된다. 프로파일을 지정하려면 애플리케이션 구동 시 -Dspring.profiles.active=myProfile1
옵션을 주면 된다(VM 옵션).
프로파일 어노테이션 표현식으로는 !(not), &(and), |(or)도 사용할 수 있다. @Profile("!prop1 & prop2 | prop3")
ii) Property
key-value 쌍으로 다양하게 정의할 수 있는 설정 값이다. VM 옵션이나 Properties 파일을 불러와서 environment.getProperty("app.name")
형태로 값을 받아 활용할 수 있다.
- VM 옵션:
-Dapp.name=spring5
- Properties:
@PropertySource("classpath:/app.properties")
프로퍼티에는 우선순위가 있다. 만약 동일한 Key값이 존재할 경우 정해진 우선순위에 따라서 값이 불러와진다.
B. MessageSource
MessageSource는 국제화(i18n) 기능을 제공하는 인터페이스이다. ApplicationContext는 MessageSource도 상속받기 때문에 MessageSource 타입으로 선언하여 ApplicationContext를 주입 받을 수 있다. 이후 messageSource.getMessage(...)
형태로 호출하여 활용한다.
Bean을 ReloadableResourceBundleMessageSource()로 재 정의하면 운영중에도 메시지를 바꿀 수 있는 환경을 구성할 수 있다.
Springboot에서는 messages.properties
, messages_ko_KR.properties
형태로 파일을 생성하면 자동으로 인식한다.
C. ApplicationEventPublisher
ApplicationEventPublisher는 이벤트를 발생시키는 기능을 제공하는 인터페이스이다. publishEvent.publishEvent(new MyEvent(...))
형태로 이벤트를 발생시킬 수 있다.
Spring 4.2 이전에는 Event는 ApplicationEvent를 상속받고, EventHandler는 ApplicationListener를 상속받아야 이벤트를 다룰 수 있었다. 4.2 이후부터는 아래와 같이 Event는 POJO로 만들고 EventHandler는 어노테이션 기반으로 작성할 수 있다.
[MyEvent.java]
public class MyEvent {
private int data;
private Object source;
public MyEvent(Object source, int data) {
this.source = source;
this.data = data;
}
public Object getSource() {
return source;
}
public int getData() {
return data;
}
}
[MyEventHandler.java]
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
...
}
}
*EventHandler는 Bean으로 등록되어야 한다.
파라미터로 MyEvent
를 받는 @EventListener
가 여러개 있을 경우 main
스레드 내에서 순차적으로(synchronized) 실행된다. 실행 순서는 보장되지 않기 때문에 @Order
어노테이션으로 실행 순서를 정의할 수 있다.
비동기적으로 실행하려면 메서드 위에 @Async
를 붙이고 메인 클래스에서 @EnableAsync
를 활성화 하면 된다.
스프링 기본 이벤트로는 ContextRefreshedEvent
, ContextStartedEvent
, ContextStoppedEvent
, ContextClosedEvent
, RequestHandledEvent
등이 있다.
D. ResourceLoader
리소스를 읽어오는 기능을 제공하는 인터페이스이다. resourceLoader를 주입받아 resourceLoader.getResource("classpath:test.txt")
형태로 활용할 수 있다.
*resources 폴더에 있는 내용은 빌드 시 classpath
로 들어가게 된다.