Spring에서는 Spring Container
, IoC Container
라는 개념을 사용한다.
Container
는 인스턴스의 생명주기를 관리하며, 생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것이다.
즉 Container
는 개발자가 작성한 코드의 처리과정을 위임받은 독립적인 존재라고 생각하면 된다.
Container
는 적절한 설정만 되어있다면 누구의 도움 없이도 작성한 코드를 스스로 참조한 뒤 알아서 객체의 생성과 소멸을 컨트롤 해준다.
Spring Container
는 Spring Framework의 핵심부에 위치하며, 종속 객체 주입을 이용하여 Application을 구성하는 Component
들을 관리한다.
이 때 Spring Container
에서 생성되는 객체를 Bean
이라고 한다.
Bean
은 Spring IoC Container
가 관리하는 자바 객체, Spring Bean Container
에 존재하는 객체를 말한다.
Spring IoC(Inversion of Control) Container
에 의해 인스턴스화, 관리, 생성된다.
Bean Container
는 의존성 주입을 통해 Bean 객체를 생성할 수 있게 해준다.
Spring에서 Bean
은 보통 Singleton
으로 존재한다.
Singleton
: 어떤 Class가 최초 한 번만 메모리를 할당하고(static) 그 메모리에 객체를 만들어서 사용하는 디자인 패턴
Spring Container
는 @Configuration
이 붙은 Class를 설정 정보로 사용한다. 여기서 @Bean
이라고 적힌 메서드를 모두 호출하여 반환된 객체를 Spring Container
에 등록한다.
이렇게 Spring Container
에 등록된 객체를 Spring Bean
이라고 한다.
Spring Bean
은 @Bean
이 붙은 메소드의 명을 Spring Bean
의 이름으로 사용한다.
이제부터는 Spring Container
를 통해서 필요한 Spring Bean
(객체)를 찾아야 한다.
Spring Bean
은 applicationContext.getBean()
메서드를 사용해서 찾을 수 있다.
기존에는 개발자가 직접 자바코드로 모든 것을 했다면, 이제부터는 Spring Container
에 객체를 Spring Bean
으로 등록하고, Spring Container
에서 Spring Bean
을 찾아서 사용한다.
@Configuration
란, 설정파일을 만들기 위한 애노테이션 or Bean
을 등록하기 위한 애노테이션이다.
Bean을 등록할때 싱글톤(singleton)이 되도록 보장해준다.
스프링컨테이너에서 Bean을 관리할수있게 됨.
//스프링 컨테이너 생성
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext
를 스프링 컨테이너라고 한다.ApplicationContext
는 인터페이스다.AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext
인터페이스의 구현체이다.참고로 스프링 컨테이너를 부를 때 BeanFactory
, ApplicationContext
로 구분해서 이야기한다. BeanFactory
를 직접 사용하는 경우는 거의 없으므로 일반적으로 ApplicationContext
를 스프링 컨테이너라 한다.
✔ 스프링 컨테이너 생성
new AnnotationConfigApplicationContext(AppConfig.class)
AppConfig.class
를 구성 정보로 지정했다.✔ 스프링 빈 등록
🔍 빈 이름
주의) 빈 이름은 항상 다름 이름을 부여해야 한다.
✔ 스프링 빈 의존관계 설정
✔ Bean 출력하기
package hello.core.beanfind;
import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + " object = " + bean);
}
}
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + " object = " + bean);
}
}
}
}
🔍 모든 빈 출력하기
ac.getBeanDefinitionNames()
: 스프링에 등록된 모든 빈 이름을 조회한다.ac.getBean()
: 빈 이름으로 빈 객체(인스턴스)를 조회한다.🔍 애플리케이션 빈 출력하기
getRole()
로 구분할 수 있다.ROLE_APPLICATION
: 일반적으로 사용자가 정의한 빈ROLE_INFRASTRUCTURE
: 스프링이 내부에서 사용하는 빈✔ 스프링 빈 조회
스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 조회 방법
ac.getBean(빈이름, 타입)
ac.getBean(타입)
조회 대상 스프링 빈이 없으면 예외 발생
NoSuchBeanDefinitionException: No bean named 'xxxxx' available
구체 타입으로 조회하면 변경시 유연성이 떨어진다
✔ 스프링 빈 조회 - 상속관계
✔ BeanFactory와 ApplicationContext
🔍 BeanFactory
스프링 컨테이너의 최상위 인터페이스다.
스프링 빈을 관리하고 조회하는 역할을 담당한다.
getBean()
을 제공한다.
🔍 ApplicationContext
BeanFactory 기능을 모두 상속받아서 제공한다.
애플리케이션을 개발할 때는 빈은 관리하고 조회하는 기능은 물론이고, 수 많은 부가기능이 필요하다.
🔍 ApplicatonContext가 제공하는 부가기능
메시지소스를 활용한 국제화 기능
환경변수
애플리케이션 이벤트
편리한 리소스 조회
🔍 정리
ApplicationContext는 BeanFactory의 기능을 상속받는다.
ApplicationContext는 빈 관리기능 + 편리한 부가 기능을 제공한다.
BeanFactory를 직접 사용할 일은 거의 없다. 부가기능이 포함된 ApplicationContext를 사용한다.
BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다.
✔ 다양한 설정 형식 지원 - 자바 코드, XML
🔍 애노테이션 기반 자바 코드 설정 사용
new AnnotationConfigApplicationContext(AppConfig.class)
AnnotationConfigApplicationContext
클래스를 사용하면서 자바 코드로된 설정 정보를 넘기면 된다
🔍 XML 설정 사용
최근에는 스프링 부트를 많이 사용하면서 XML기반의 설정은 잘 사용하지 않는다.
아직 많은 레거시 프로젝트 들이 XML로 되어 있고, 또 XML을 사용하면 컴파일 없이 빈 설정 정보를 변경할 수 있는 장점이 있다.
GenericXmlApplicationContext
를 사용하면서 xml 설정 파일을 넘기면 된다.
✔ 스프링 빈 설정 메타 정보 - BeanDefinition
스프링은 이런 다양한 설정 형식을 지원할 수 있는 이유는 BeanDefinition
이라는 추상화 때문이다.
쉽게 이야기해서 역할과 구현을 개념적으로 나눈 것이다.
XML을 읽어서 BeanDefinition
을 만들면 된다.
자바 코드를 읽어서 BeanDefinition
을 만들면 된다.
스프링 컨테이너는 자바 코드인지, XML인지 몰라도 된다. 오직 BeanDefinition
만 알면 된다.
BeanDefinition
을 빈 설정 메타정보라 한다.
@Bean
, <bean>
당 각각 하나씩 메타 정보가 생성된다.
스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.
🔍 스프링 빈 설정 메타 정보-심화
AnnotationConfigApplicationContext
는 AnnotatedBeanDefinitionReader
를 사용해서AppConfig.class
를 읽고 BeanDefinition
을 생성한다.
GenericXmlApplicationContext
는 XmlBeanDefinitionReader
를 사용해서 appConfig.xml
설정 정보를 읽고 BeanDefinition
을 생성한다.
새로운 형식의 설정 정보가 추가되면, XxxBeanDefinitionReader
를 만들어서 BeanDefinition
을 생성하면 된다.
🔍 BeanDefinition 정보
🔍 정리
BeanDefinition
을 직접 생성해서 스프링 컨테이너에 등록할 수 도 있다.
하지만 실무에서 BeanDefinition
을 직접 정의하거나 사용할 일은 거의 없다. 어려우면 그냥 넘어가면 된다.
BeanDefinition
에 대해서는 너무 깊이있게 이해하기 보다는, 스프링이 다양한 형태의 설정 정보를 BeanDefinition
으로 추상화해서 사용하는 것 정도만 이해하면 된다.
가끔 스프링 코드나 스프링 관련 오픈 소스의 코드를 볼 때, BeanDefinition
이라는 것이 보일 때가 있다.
이때 이러한 메커니즘을 떠올리면 된다.
스프링 빈을 만드는 방법은 크게 2가지가 있다.
하나는 직접적으로 스프링 빈을 등록하는 방법이다.
나머지는 팩토리 빈을 통해서 등록하는 방법이다.