Spring Framework/스프링 핵심 원리
3. 스프링 컨테이너와 스프링 빈
hnjee
2025. 4. 4. 10:37
1. 스프링 컨테이너와 스프링 빈
1) IoC Container
- IoC: Inversion of Control, 제어의 역전
- 프로그래밍 제어의 흐름을 개발자가 아닌 프레임워크에서 처리하는 것
- 구현 객체를 생성하고, 연결하고, 실행하는 것은 프레임 워크에 맡기고 개발자는 로직을 구현하는 것에 집중
- IoC Container
- IoC를 구현한 구체적인 프레임워크
- IoC Container는 객체의 생성, 초기화, 의존성 처리 등을 자동으로 수행해준다.
- 의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라 한다. 또는 어샘블러, 오브젝트 팩토리 등으로 불리기도 한다.
- 대표적인 IoC Container로는 Spring Framework의 ApplicationContext 가 있다.
2) Bean, BeanFactory, Configuration Metadata
- POJO(Plain Old Java Object): 일반적인 자바 객체
- Bean: Spring IoC Container에서 관리되는 객체
- Bean Factory: Spring에서 사용되는 IoC Container의 가장 기본적인 형태로, Bean의 생성, 초기화, 연결, 제거 등의 라이프사이클을 관리한다.
- Configuration Metadata: BeanFactory가 Bean 객체를 생성하고 구성하기 위해 사용하는 설정 정보
- 스프링 컨테이너를 생성할 때 메타 정보와 POJO가 필요하다
- 스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계되어 있다.
3) ApplicationContext
- BeanFactory: 스프링 컨테이너의 최상위 인터페이스
- ApplicationContext: 빈 관리 기능 + 편리한 부가 기능 추가
BeanFactory를 직접 사용할 일은 거의 없고, 부가 기능이 추가된 ApplicationContex를 사용한다.
- ListableBeanFactory : BeanFactory가 제공하는 모든 기능을 포함한다.
- ApplicationEventPublisher : 이벤트 처리(Event Handling) 기능을 제공한다.
- MessageSource : 국제화(i18n) 를 지원하는 메세지를 해결하는 부가 기능을 제공한다.
- ResourceLoader : 리소스 핸들링(Resource Handling) 기능을 제공한다.
- ApplicationContext 구현 클래스
스프링 컨테이너는 XML을 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로도 만들 수 있다.- AnnotationConfigApplicationContext : Java MetaData Configuration을 읽어 컨테이너 역할을 수행한다.
- GenericXmlApplicationContext : XML MetaData Configuration을 읽어 컨테이너 역할을 수행한다.
- BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다.
2. 스프링 컨테이너 생성과 스프링 빈 조회
1) 스프링 컨테이너 생성 과정
// 애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너 생성
ApplicationContext ac =
new AnnotationConfigApplicationContext(AppConfig.class);
(1) 스프링 컨테이너 생성
→ (2) 구성 정보(AppConfig.class) 활용하여 스프링 빈 등록
→ (3) 스프링 빈 의존관계 주입
빈 이름은 메서드 이름을 사용하지만, 직접 설정할 수도 있다. 반드시 빈 이름은 각자 다르게 사용해야 함.
스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있다.
그런데 자바 코드로 스프링 빈을 등록하는 경우에는 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.
2) 컨테이너에 등록된 Bean 조회
//컨테이너에 등록된 모든 빈 조회
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
//빈 이름으로 조회 + 애플리케이션 빈만 출력하기
String beanDefinitionName = beanDefinitionNames[0];
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
Object bean = ac.getBean(beanDefinitionName);
}
//이름+타입으로 조회
MemberService memberService = ac.getBean("memberService", MemberService.class);
//이름+구체 타입으로 조회
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
//타입으로 조회 -> 같은 타입 둘 이상이면 중복 오류 발생
MemberService memberService = ac.getBean(MemberService.class);
//특정 타입 모두 조회
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
//부모 타입으로 조회시 자식까지 다 조회됨 -> 자식 둘 이상이면 중복 오류 발생
DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
- 컨테이너에 등록된 모든 Bean 조회
- ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회한다
- ac.getBean() : 빈 이름으로 빈 객체(인스턴스)를 조회한다.
- getRole()
- ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
- ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
- 기본 Bean 조회
- ac.getBean(빈이름, 타입)
- ac.getBean(타입)
- 조회 대상 스프링 빈이 없으면 예외 발생
NoSuchBeanDefinitionException: No bean named 'xxxxx' available
- 동일한 타입이 둘 이상인 경우
- 타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생한다. 이때는 빈 이름을 지정하자.
- ac.getBeansOfType() : 해당 타입의 모든 빈을 조회할 수 있다.
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
- 상속 관계
- 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
- 그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.
3. 다양한 설정 형식 지원
스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계되어 있다.
단, 최근에는 스프링 부트를 많이 사용하면서 XML기반의 설정은 잘 사용하지 않는다.
//1. 애노테이션 기반 자바 코드 설정 사용
//AnnotationConfigApplicationContext 클래스를 사용하면서 자바 코드로된 설정 정보를 넘긴다.
ApplicationContext ac =
new AnnotationConfigApplicationContext(AppConfig.class);
//2. XML 설정 사용
//GenericXmlApplicationContext 클래스를 사용하면서 xml 설정 파일을 넘긴다.
ApplicationContext ac =
new GenericXmlApplicationContext("appConfig.xml");
// 자바코드 설정 정보: AppConfig.class
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
//xml 설정 정보 : appConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="memberService" class="spring.core.member.MemberServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository" />
</bean>
<bean id="memberRepository"
class="spring.core.member.MemoryMemberRepository" />
<bean id="orderService" class="spring.core.order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository" />
<constructor-arg name="discountPolicy" ref="discountPolicy" />
</bean>
<bean id="discountPolicy" class="spring.core.discount.RateDiscountPolicy" />
</beans>
4. 스프링 빈 설정 메타 정보 - BeanDefinition
1) BeanDefinition이란?
- 스프링이 이처럼 다양한 설정 형식을 지원할 수 있는 이유는 'BeanDefinition'이라는 추상화이다.
- BeanDefinition은 Bean 설정 메타정보(Configuration Metadata)
- 스프링은 여러 형식(자바 코드, XML 등)의 설정 정보를 읽고 BeanDefinition을 생성할 수 있다.
@Bean, <bean> 당 각각 하나씩 메타 정보가 생성된다. - 스프링 컨테니어는 BeanDefinition를 기반으로 스프링 빈을 생성한다.
- 코드 레벨에서의 BeanDefinition과 Bean 생성 과정
(1) XxxBeanDefinitionReader가 xxx형식의 설정 정보를 읽고 BeanDefinition를 생성
(2) XxxApplicationContext 스프링 컨테이너가 BeanDefinition을 기반으로 Bean을 생성
2) BeanDefinition 정보
void findApplicationBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition
= ac.getBeanDefinition(beanDefinitionName);
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
System.out.println("beanDefinitionName" + beanDefinitionName
+ "beanDefinition = " + beanDefinition);
}
}
}
- BeanClassName: 생성할 빈의 클래스 명(자바 설정 처럼 팩토리 역할의 빈을 사용하면 없음)
- factoryBeanName: 팩토리 역할의 빈을 사용할 경우 이름, 예) appConfig
- factoryMethodName: 빈을 생성할 팩토리 메서드 지정, 예) memberService
- Scope: 싱글톤(기본값)
- lazyInit: 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아니라, 실제 빈을 사용할 때 까지 최대한 생성을 지연 처리 하는지 여부
- InitMethodName: 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드 명
- DestroyMethodName: 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드 명
- Constructor arguments, Properties: 의존관계 주입에서 사용한다. (자바 설정 처럼 팩토리 역할의 빈을 사용 하면 없음)