[스프링 빈 생성 과정 분석 시리즈]
- 스프링 빈 생성 과정 분석 [1] - Application Context(BeanFactory)
- 스프링 빈 생성 과정 분석 [2] - BeanDefinitionRegistry, SingletonBeanRegistry
- 스프링 빈 생성 과정 분석 [3] - BeanFactoryPostProcessor, BeanPostProcessor
- 스프링 빈 생성 과정 분석 [4] - 디버깅 참고 자료
ApplicationContext(IOC 컨텍스트) 소개
스프링의 application context는 ioc 컨텍스트라고도 불리운다.
스프링에서 ioc의 개념이 적용된 대표적인 예는 DI이다.
DI(Dependency Injection)는 ①의존 객체를 결정하고, ②의존 객체를 생성하고, ③객체 주입을 대신해준다.
우리는 이를 코드로 구현하지 않아도 되며 이에 따른 변경사항도 코드를 통해 수정하지 않아도 된다.
어노테이션이나 xml 등을 통해 설정하면 스프링에서 위 과정을 대신 처리해준다.
스프링에서 DI의 대상이 되는 객체를 스프링 빈이라고 한다.
스프링 빈을 생성 및 관리하는 책임을 갖는 인터페이스가 BeanFactory인데 ApplicationContext의 상위 타입이다.
ApplicationContext의 이외에도 다양한 책임들을 갖고 있다.
포스팅 1편에서는 간단하게 빈 생성 및 관리 이외에도 컨텍스트에서 무슨 기능을 제공해주는지 알아보도록 하자.
IOC (Inversion of Control, 제어의 역전)
제어의 역전이란 프로그램의 제어 흐름 구조를 바꾸는 것이다.
프로그램의 제어권이 개발자가 작성한 코드에 있는 것이 아니라 외부에 존재하는 것을 말한다.
프로그램의 제어권에는 객체 생성, 사용할 객체 결정, 객체 주입, 메서드 호출 등 프로그래밍 언어로 표현할 수 있는 모든 행위를 포함한다.
ApplicationContext의 상속 관계
ApplicationContext는 위와 같은 인터페이스를 상속 받고 있다.
- BeanFactory : 빈의 생성, 스코프, 별칭 등을 관리
- MessageSource : 다국어 메시지 처리
- EnvironmentCapable : 애플리케이션 환경변수 참조
- ResourceLoader : 클래스 경로, 파일 접근
- ApplicationEventPublisher : 이벤트 리스너에게 이벤트 발행
여기에서 IOC 개념이 적용되어있는 것은 BeanFactory와 ApplicationEventPublisher 뿐이다.
나머지 MessageSource, EnvironmentCapable, ResourceLoader 는 라이브러리와 같이 기능을 제공할 뿐이지 애플리케이션의 흐름을 제어하는 것은 아니다.
ApplicationContext의 상위 타입
i) MessageSource
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
다국어 메시지를 처리할 수 있는 getMessage 메서드로 이루어져있는 인터페이스이다
code는 메시지 속성명, args는 메시지에 추가할 수 있는 인자값, Locale은 국가이다.
[예제]
messages.properties
name=Hello, {0} nice to meet you |
messages_ko_KR.properties
name=Hello, {0} nice to meet you |
위와 같이 파일을 만들고 테스트코드를 작성하면
@Autowired
lateinit var messageSource: MessageSource
@Test
fun `메시지 테스트`() {
Locale.setDefault(Locale("en", "US"))
println(messageSource.getMessage("name", arrayOf("tony"), Locale.getDefault()))
println(messageSource.getMessage("name", arrayOf("토니"), Locale.KOREA))
}
[출력]
Hello, tony nice to meet you
안녕, 토니 만나서 반가워
이와 같이 LOCAL에 따라 다국적 메시지를 출력할 수 있다.
운영환경이라면 서버 Local 설정을 그대로 따라갈 것이다.
ii) EnvironmentCapable
getEnviroment() 메서드 하나로 이루어져 있다.
Environment getEnvironment();
getEnviroment() 의 리턴타입인 Environment를 통해 application.properties나 application.yml에 정의한 프로젝트 환경변수에 접근이 가능하다.
[예제]
application-test.yml
spring: application: name: learningExample |
@SpringBootTest
@ActiveProfiles("test")
class IocContextTest {
@Autowired
private lateinit var context: ApplicationContext
@Test
fun `environment 테스트`() {
val environment = context.environment
environment.activeProfiles.forEach {
println(it.toString())
}
println(environment.getProperty("spring.application.name"))
}
}
[출력]
test
learningExample
iii) ResourceLoader
ResourceLoader는 클래스 경로나 파일에 대한 접근을 가능하게 하는 인터페이스이다.
Resource getResource(String location);
getResource 메서드에 경로를 인자로 넣으면 Resource를 반환해주는데 Resource는 getUri나 getFile과 같이 파일에 대한 직접적인 접근 메서드를 정의하고 있다.
[예제]
resources 하위에 abc가 입력된 text.txt 작성
@SpringBootTest
class ResourcesTest {
@Autowired
lateinit var resourceLoader: ResourceLoader
@Test
fun `리소스 파일 추출`() {
val pattern = "classpath:text.txt"
val resource: Resource = resourceLoader.getResource(pattern)
println(resource.exists())
println(resource.uri)
println(resource.filename)
println(resource.getContentAsString(Charset.defaultCharset()))
}
}
[출력]
true
file:/C:/경로/LearningExample/spring-study/build/resources/main/text.txt
text.txt
abc
iv) ApplicationEventPublisher
이벤트 발행을 제공하는 인터페이스이다.
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object event);
publishEvent 메서드로 구성 되어있는데 과거에는 ApplicationEvent를 상속해야 이벤트 객체로 사용할 수 있었으나 이제는 별도의 상속 없이도 이벤트 객체로 사용가능하다.
리스너는 오직 event 객체에만 의존하고 이벤트 발행 대상도 알지 못한다.(객체간 의존관계 없음)
호출자와 피호출자의 의존도가 굉장히 낮은 구조이다. 주로 리턴값 필요 없고 부가 로직을 수행하는데 적용하기 좋다.
publishEvent가 메서드가 실행되면 프레임워크에서 해당 이벤트 객체 타입을 처리하는 이벤트 리스너를 가져와 메서드를 실행시켜주는 방식으로 흐름을 제어한다.
(잠깐, 딴소리)
|
[예제]
class AppleEvent(
val name: String,
)
@SpringBootTest
class ObserverTest {
@Autowired
lateinit var eventPublisher: ApplicationEventPublisher
@Test
fun `이벤트 발행-구독 옵저버 패턴 테스트`() {
eventPublisher.publishEvent(AppleEvent("청송사과"))
}
}
@Component
class FruitEventListener {
@Order(1)
@Async
@EventListener
fun handleAppleEvent(apple: AppleEvent) {
println("과일의 한 종류인 ${apple.name} 이벤트 리슨")
}
}
@Component
class AppleEventListener {
@Order(2)
@Async
@EventListener
fun handleAppleEvent(apple: AppleEvent) {
println("사과의 한 종류인 ${apple.name} 이벤트 리슨")
}
}
[출력]
과일의 한 종류인 청송사과 이벤트 리슨
사과의 한 종료인 청송사과 이벤트 리슨
v) BeanFactory
대부분의 메서드가 getBean() 메서드의 오버로딩 형식을 통해 정의되어있고,
singleton이나 prototype과 같은 스코프 범위, aliases같은 빈의 별칭 또한 정의 되어있다.
따라서 스프링 BeanFactory의 빈 정의는 빈의 이름, 스코프(싱글톤, 프로토타입), 클래스 타입, 별칭이다.
Object getBean(String name) throws BeansException;
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
구체적인 내용은 구현체를 다루면서 진행하겠으나
getBean 메서드는 빈이 존재하지 않으면 빈을 생성하여 제공하는 방식으로 구현되어있다.
추가적인 개념으로 BeanFactory를 상속하며 ApplicationContext의 상위타입인 아래 두 BeanFactory에 대해서도 알아보겠다.
vi) ListableBeanFactory
BeanFactory에 정의된 메서드와 다르게 리턴값이 배열타입이다. 갯수 제공 메서드도 있다.
int getBeanDefinitionCount();
String[] getBeanDefinitionNames();
String[] getBeanNamesForType(@Nullable Class<?> type);
스프링 docs에서 설명하는 ListatblaBeanFactory 정의는 빈의 이름으로 하나씩 요청해야하는 BeanFactory와 달리 모든 빈 인스턴스를 열거할 수 있다고 설명한다
또 하나의 차이점은 BeanDefinition이라는 용어가 등장한다.
BeanDefinition은 빈의 scope, 별칭, primary, lazy init과 같은 속성이다.
스프링 부트 auto-config 방식에서 사용되는 beanFactory가 ListableBeanFactroy의 구현체이기에
Primary, Qualifier, Lazy와 같은 어노테이션으로 빈의 관리 방식을 정의할 수 있는 것이다.
스프링의 빈은 BeanDefinition 기반으로 인스턴스화되어 관리된다.
이에 대한 자세한 설명은 BeanDefinition이 어떻게 생성되고 관리되는지와 빈 인스턴스화 과정을 설명하며 함께 진행 하겠다.
'Framework & Lib > 스프링' 카테고리의 다른 글
스프링 빈 생성 과정 분석 [3] - BeanFactoryPostProcessor, BeanPostProcessor (2) | 2024.04.20 |
---|---|
스프링 빈 생성 과정 분석 [2] - BeanDefinitionRegistry, SingletonBeanRegistry (0) | 2024.04.20 |
[스프링 개념] 동일 타입 빈, 빈 이름 중복, DI 구현 방법 (0) | 2024.01.02 |
[스프링 개념] IOC, DI와 DIP (0) | 2023.12.31 |
스프링 시큐리티 개념 정리 (1) | 2023.12.06 |