-
Spring IoC Container 알아보기Language/Spring 2023. 10. 30. 10:19728x90반응형
- 목차
관련된 글
https://westlife0615.tistory.com/7
소개.
IoC Container 는 Bean Container 또는 Spring Container 라고 불립니다.
IoC Container 는 Spring Bean 을 관리하는 Spring 의 요소로써, Bean 의 라이프사이클을 관리하며 책임집니다.
흔히 Bean 을 managed Java object 라고 부르죠.
여기서 말하는 managed 가 바로 IoC Container 에 의해 관리됨을 뜻합니다.
IoC 는 Inversion of Control 의 줄임말입니다.
IoC 는 제어권을 뒤바뀐다는 의미인데,
java object 의 생성을 프로그래밍 방식으로 행하던 방식과 달리 설정을 통해서 java object 가 생성됩니다.
그러니까 new Object() 처럼 매뉴얼하게 생성하는 것이 아닌 XML 이나 어노테이션을 통한 설정으로 객체들이 생성됩니다.
이렇게 객체를 생성하는 것은 다른 표현으로 Dependency Injection 이라고 합니다.
Spring 의 Bean 들은 사용자의 코드가 아닌 설정에 의해 생성되므로 Dependency Injection 방식이 달라집니다.
이러한 역전 현상을 IoC 라고 합니다.
과거에서 현재까지 Dependency Injection 방식은 변화를 거듭합니다.
XML 기반의 설정부터 현재는 Annotation 을 통한 방식으로 변해왔습니다.
이어지는 내용에서 IoC Container 에 대해서 상세히 알아보도록 하겠습니다.BeanFactory & ApplicationContext.
저는 처음에 IoC Container 에 대해서 들었을 때,
Bean 을 관리하는 역할을 하는 Spring 의 컴포넌트라고 알고 있었습니다.
그래서 무언가 엄청 거창한 존재라고 느꼈었고,
그래서 Linux Process 또는 어떤 특별한 자료구조라고 추측하고 있었습니다.
사실, IoC Container 는 BeanFactory 또는 ApplicationContext 라는 interface 를 구현한 java class 이자
그 class 를 객체화시킨 인스턴스입니다.
그러니깐 new BeanFactory() 또는 new ApplicationContext() 와 같은 형식으로 생성하는 java object 인거죠.
그럼 BeanFactory 와 ApplicationContext 는 무엇일까요 ?
이들은 java 의 interface 인데요.
먼저 어떻게 생긴 interface 인지 알아보겠습니다.
<BeanFactory Interface>public interface BeanFactory { Object getBean(String name) throws BeansException; Object getBean(String name, Class<?> requiredType, Object... args) throws BeansException; String[] getBeanNames(); boolean containsBean(String name); boolean isSingleton(String name) throws NoSuchBeanDefinitionException; boolean isPrototype(String name) throws NoSuchBeanDefinitionException; Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name); boolean isCurrentlyInCreation(String name); void destroySingletons(); }
<ApplicationContext Interface>public interface ApplicationContext extends BeanFactory { ResourceBundle getResourceBundle(); MessageSource getMessageSource(Locale locale); ApplicationEventPublisher getApplicationEventPublisher(); ResourceLoader getResourceLoader(); ConfigurableEnvironment getEnvironment(); ApplicationContext getParent(); URL getWebServerRootUrl(); void close() throws BeansException; void refresh() throws BeansException; }
interface 가 대략적으로 어떻게 생겼는지 살펴보았다면, 하나씩 어떤 의미를 가지는지 알아보겠습니다.
BeanFactory.
BeanFactory 는 이름 그대로 Bean 을 생성하는 공장입니다.
예를 들어,
BeanFactory 를 구현하는 클래스의 getBean 메소드 내부에 Bean 을 생성하여 반환하는 코드가 채워질 것입니다.
이러한 방식으로 BeanFactory 를 통해서 IoC Container, Bean Container 가 생성됩니다.
BeanFactory 를 구현하는 클래스들은
- XmlBeanFactory
- DefaultListableBeanFactory
등이 있습니다.
즉, Spring Application 이 구동될 때 XML 또는 Annotation 의 설정을 통해서 모든 Bean 이 생성되구요.
생성되는 과정에서 BeanFactory 가 사용됩니다.
ApplicationContext.
ApplicationContext 는 BeanFactory 를 상속하는 인터페이스입니다.
ApplicationContext 가 BeanFactory 를 상속한다는 의미는
BeanFactory 의 Bean 을 관리하는 역할 뿐만 아니라 다른 역할도 수행할 수 있다는 의미입니다.
ApplicationContext 가 수행하는 다른 역할들은 아래와 같습니다.
- internationalization and localization
- resource management
- event publishing and listening
위의 기능들은 "음, 이런 기능들이 있군.", "ApplicationContext 는 Bean 관리 뿐만 아니라 다른 일도 하는군" 정도만 아셔도 될 것 같습니다.
그럼 왜 ApplicationContext 라고 이름이 붙여졌을까요 ?
Context 라는 의미는 여러 분야에서 사용됩니다.
Context Switching.
일반적인 OS 는 멀티프로세싱을 지원합니다.
이는 Time-Shared 방식으로 여러 프로세스가 CPU 는 점유하게 되는데,
각 Process 들은 자신의 Process Control Block 이라는 자료구조에 CPU 를 점유했던 상황을 기록합니다.
- 레지스터에는 어떤 값들로 구성되고
- Process 가 관리하던 쓰레드들은 어떤 Call Stack 을 가지며
- Process 가 작업했던 File 과 Socket 등에 대한 정보
등이 존재합니다.
한 Process 가 다른 Process 에게 CPU 를 빼앗길 때, 위의 정보들을 PCB 에 저장하고,
이러한 개념을 Context 라고 부릅니다.
React Context.
React 에 Context 라는 개념이 있습니다.
여러 컴포넌트이 공유하는 데이터 또는 상태의 저장소인데요.
일반적인 View 구조에서 이벤트의 전달을 통해서 부모 컴포넌트 (또는 뷰)와 자식 컴포넌트가 소통을 할 수 있습니다.
하지만 Context 라는 데이터 또는 상태 저장소를 통해서 컴포넌트 (또는 뷰)들 사이의 소통이 가능해집니다.
React 에서는 연관있는 컴포넌트 사이의 상태 저장소 및 소통을 위한 창구를 Context 라고 합니다.
Flink Context.
Flink 는 Runtime Context 하는 개념이 존재합니다.
말 그래로 Runtime 의 문맥 또는 Runtime 의 환경 정보를 의미하는데요.
실제 Flink Application 이 구동되는 상태에서 RuntimeContext 를 통해
어떤 Subtask 또는 어떤 TaskManager 에서 자신이 실행 중인지에 대한 정보를 얻을 수 있습니다.
다시 돌아와서...
Spring 의 ApplicationContext 는 Bean 를 관리하는 역할을 넘어서
Spring Application 의 시스템적인 기능을 수행할 수 있도록 여러 역할에 관여합니다.
그래서 ApplicationContext 라고 불린다고 생각하시면 됩니다.
ApplicationContext 를 구현하는 클래스들은 아래와 같습니다.
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
- WebXmlApplicationContext
- AnnotationConfigApplicationContext
- GenericApplicationContext
등등
@Autowired.
@Autowired 를 통해서 손쉽게 Dependency Injection 을 구현할 수 있습니다.
Autowired 의 종류는 Field Injection 와 Constructor Injection 이 있습니다.
Field Injection.
Field Injection 은 @Autowired 어노테이션을 Field 에 마킹합니다.
이를 통해서 해당 Field 에 Bean 에 세팅됩니다.
자세한 예시는 아래와 같습니다.
TestBean 는 TestChildBean 을 Dependency 로써 가집니다.
TestChildBean 는 @Autowired 를 통해서 간편하게 Injection 됩니다.
<예시 코드>package org.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Component; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication application = new SpringApplication(AppConfig.class); ConfigurableApplicationContext context = application.run(args); TestBean testBean = context.getBean(TestBean.class); System.out.printf("childBean is %s", testBean.getChildBean().hashCode()); } } @ComponentScan("org.example") class AppConfig { } @Component class TestBean { public String name; @Autowired private TestChildBean childBean; public TestChildBean getChildBean() { return childBean; } } @Component class TestChildBean { public String name; }
Field Injection 은 내부적으로 Reflection 을 사용합니다.
@Autowired 어노테이션이 부착된 Field 에 한해서 IoC Container 로부터 적절한 Bean 을 조회하여
해당 Bean 을 Reflection 을 통해서 설정합니다.
아래는 Reflection 을 통해서 Autowired 를 구현한 예시입니다.
<Reflection 예시 코드>package org.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Component; import java.lang.reflect.Field; @SpringBootApplication public class Main { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { SpringApplication application = new SpringApplication(AppConfig.class); ConfigurableApplicationContext context = application.run(args); TestBean testBean = context.getBean(TestBean.class); System.out.printf("Autowired childBean is %s \n", testBean.getChildBean().hashCode()); TestBean newTestBean = new TestBean(); Field childBean = TestBean.class.getDeclaredField("childBean"); childBean.setAccessible(true); TestChildBean testChildBean = context.getBean(TestChildBean.class); childBean.set(newTestBean, testChildBean); System.out.printf("Reflection childBean is %s \n", newTestBean.getChildBean().hashCode()); } } @ComponentScan("org.example") class AppConfig { } @Component class TestBean { public String name; @Autowired private TestChildBean childBean; public TestChildBean getChildBean() { return childBean; } } @Component class TestChildBean { public String name; }
<실행 결과>
Autowired 와 Reflection 두가지 케이스의 결과는 동일합니다.Autowired childBean is 1627781283 Reflection childBean is 1627781283
Constructor Injection.
Constructor Injection 은 Dependency 를 인자로 가지는 생성자를 통한 Dependency Injection 입니다.
Field Injection 시에 @Autowired 를 Field 에 설정했다면,
Constructor Injection 은 @Autowired 를 생성자에 적용합니다.
예시는 아래와 같습니다.
package org.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Component; import java.lang.reflect.Field; @SpringBootApplication public class Main { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { SpringApplication application = new SpringApplication(AppConfig.class); ConfigurableApplicationContext context = application.run(args); TestBean testBean = context.getBean(TestBean.class); System.out.printf("childBean is %s \n", testBean.getChildBean().hashCode()); TestBean newTestBean = new TestBean(null); Field childBean = TestBean.class.getDeclaredField("childBean"); childBean.setAccessible(true); TestChildBean testChildBean = context.getBean(TestChildBean.class); childBean.set(newTestBean, testChildBean); System.out.printf("childBean is %s \n", newTestBean.getChildBean().hashCode()); } } @ComponentScan("org.example") class AppConfig { } @Component class TestBean { public String name; private TestChildBean childBean; @Autowired public TestBean(TestChildBean childBean) { this.childBean = childBean; } public TestChildBean getChildBean() { return childBean; } } @Component class TestChildBean { public String name; }
<실행 결과>childBean is 1340051218 childBean is 1340051218
반응형'Language > Spring' 카테고리의 다른 글
Spring Batch Job 알아보기 (3) 2023.11.24 Spring Bean 알아보기 (2) 2023.10.30