일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- 일급 컬렉션
- Spring
- Volatile
- synchronized
- nestjs
- lombok
- Google OAuth
- middleware
- OAuth 2.0
- 일급 객체
- builder
- Dependency Injection
- spring security
- factory
- java
- Today
- Total
HJW's IT Blog
스프링 컨테이너와 스프링 빈 본문
스프링 컨테이너 생성
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext 를 Spring Container 라 한다. BeanFactory 도 있지만, 거의 사용하지 않는다.
Spring Container는 XML 을 기반으로 만들 수 있고, 어노테이션 기반의 자바 설정 클래스로 만들 수 있다.
AnnotationConfigApplicationContext 는 ApplicationContext interface 의 구현체이다.
생성 과정
1.Spring Container 생성

이 상태는 구성 정보가 지정되어 있지 않는 컨테이너가 생성된다. 이를 활용하기 위해 Spring Container를 생성할 때는, 구성 정보를 지정해주어야 한다.
2.Spring Bean 등록

Bean 이름은 메서드 이름을 기본적으로 사용하고, 직접 이름을 부여할 수도 있지만, 반드시 unique 한 이름을 부여해야 한다. 같은 이름을 부여하게 되면, Bean 이 무시되거나 덮어쓴느 경우가 발생한다.
3&4. Spring Bean 의존관계 설정 준비 및 완료

컨테이너에 등록된 모든 Bean 조회
public class ApplicationContextInfoText {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
void findAllBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for(String beanDefinitionName: beanDefinitionNames){
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name= " + beanDefinitionName + " object= " + bean);
}
}
@Test
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 + " objec= " + bean);
}
}
}
}
Spring Container 는 내부적으로 사용하는 Bean 들도 있다. 이를 제외하고 내가 등록한 Bean 들만 보고 싶다면, 두번째 test 의 BeanDefinition.ROLE_APPLICATION 을 활용하자.
Bean 조회
스프링 컨테이너에서 Bean 을 찾는 가장 기본적인 조회 방법에 대해 알아보자.
- ac.getBean(beanName, type)
- ac.getBean(type)
- 조회 대상이 없다면 예외가 발생한다.
package hello.core.beanfind;
import hello.core.AppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);
@Test
void findBeanByName(){
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
void findBeanByType(){
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
void findBeanByName2(){
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
void findByBeanNameX(){
assertThrows(NoSuchBeanDefinitionException.class, () ->{
ac.getBean("xxx", MemberService.class);
});
}
}
동일한 타입이 둘 이상인 상황
package hello.core.beanfind;
import hello.core.AppConfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.*;
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
void findBeanByTypeDuplicate(){
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> {
ac.getBean(MemberRepository.class);
});
}
@Test
void findBeanByName(){
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
@Test
void voidAllBeanByType(){
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for(String key : beansOfType.keySet()){
System.out.println("key= " + key + " value = " + beansOfType.get(key));
}
System.out.println(beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}
@Configuration
static class SameBeanConfig{
@Bean
public MemberRepository memberRepository1(){
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2(){
return new MemoryMemberRepository();
}
}
}
Spring Bean 상속관계
부모 타입으로 조회를 하면, Spring Bean 은 자식 type 도 함께 조회한다. 그래서 모든 JAVA 객체의 최고 부모인 Object type 으로 조회하면, 모든 Spring Bean 을 조회한다.

package hello.core.beanfind;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import java.util.Objects;
import static org.assertj.core.api.Assertions.*;
public class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(TestConfig.class);
@Test
void findBeanByParentTypeDuplicate(){
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> {
ac.getBean(DiscountPolicy.class);
});
}
@Test
void findBeanByParentTypeBeanName(){
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
void findBeanBySubType(){
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}
@Test
void findAllBeanByParentType(){
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
for(String key : beansOfType.keySet()){
System.out.println("key = " + key + " value=" +
beansOfType.get(key));
}
}
@Test
void findAllBeanByObjectType(){
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
for(String key : beansOfType.keySet()){
System.out.println("key = " + key + " value=" +
beansOfType.get(key));
}
}
@Configuration
static class TestConfig {
@Bean
public DiscountPolicy rateDiscountPolicy(){
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy(){
return new FixDiscountPolicy();
}
}
}
BeanFactory 와 ApplicationContext

BeanFactory
- Spring Container 의 최상위 interface
- Spring Bean 을 관리하고 조회하는 역할을 담당한다
- getBean() 등을 제공한다
ApplicationContext
- BeanFactory를 모두 상속받아 제공한다
- BeanFactory 에 추가로 다음과 같은 기능을 제공한다
Spring Bean Meta Data - BeanDefinition
Spring 은 여러 설정 형식을 지원하는데, 이는 BeanDefinition 이라는 추상화가 있기에 가능하다. 즉, Bean 자체에 대해 추상화를 하는 것이다.
XML 이든, JAVA 코드이든, 스프링 컨테이너는 이에 대해 알 필요가 없고 오직 BeanDefinition 만 알면 된다.
이러한 BeanDefinition을 빈 설정 메타 정보라 한다.


AnnotationConfigApplicationContext 는 AnnotatedBeanDefinitionReader 를 사용하여 AppConfig.class 를 읽고 BeanDefinition을 생성한다. GeneficXmlApplicationContext 의 경우, XmlBeanDefinitionReader 를 사용하여 BeanDefinition을 생성한다.
BeanDefinition 살펴보기
BeanDefinition 정보
- BeanClassName : 생성할 bean의 Class Name
- factoryBeanName : 팩토리 역할의 bean을 사용할 경우 이름
- factoryMethodName : bean을 생성할 팩토리 메서드 지정
- Scope : Singleton (Default)
- lazyInit : Spring Container 를 생성할 때 bean 을 생성하는 것이 아니라, 실제 bean을 사용할 때 까지 최대한 생성 지연 처리 여부
- InitMethodName : bean 을 생성, 의존관계를 적용한 뒤 호출되는 초기화 메서드
- DestroyMethodName : Bean의 생명주기가 끝나서 제거하기 직전 호출되는 메서드
public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);
@Test
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for(String beanDefinitionName: beanDefinitionNames){
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
System.out.println("beanDefName= " + beanDefinitionName + " beanDef= " + beanDefinition);
}
}
}
}
beanDefName= appConfig beanDef= Generic bean: class=hello.core.AppConfig$$SpringCGLIB$$0; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null
beanDefName= memberService beanDef= Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=appConfig; factoryMethodName=memberService; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.core.AppConfig
beanDefName= orderService beanDef= Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=appConfig; factoryMethodName=orderService; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.core.AppConfig
beanDefName= memberRepository beanDef= Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=appConfig; factoryMethodName=memberRepository; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.core.AppConfig
beanDefName= discountPolicy beanDef= Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=appConfig; factoryMethodName=discountPolicy; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.core.AppConfig
'SPRING' 카테고리의 다른 글
컴포넌트 스캔과 의존성 자동 주입 (0) | 2024.12.04 |
---|---|
스프링 싱글톤 컨테이너의 동작 원리 (0) | 2024.12.02 |
객체 지향 설계와 스프링 (2) | 2024.11.27 |
AOP 기본 개념 정리 (1) | 2024.11.27 |
Spring Bean 과 의존관계 설정 두가지 방법 (0) | 2024.11.25 |