[Spring] 스프링 컨테이너 - ApplicationContext, Bean 조회

2025. 6. 16. 16:45·Spring/Core

스프링을 사용하면서, 각 컴포넌트별로 인터페이스들의 구현체 의존성을 자동으로 주입하고, 관리하는 컨테이너를 스프링 컨테이너라고 한다. 


스프링 컨테이너 생성

ApplicationContext ac = new AnnotationConfigApplicationCOntext(AppConfig.class);

우리는 ApplicationContext를 스프링 컨테이너라고 하고 이는 인터페이스이며, 이는 다양한 방식을 기반으로 생성한 구현체를 주입할 수 있다 (에노테이션, XML 등)

어떤 구현체를 사용할지 명세 해놓은 AppConfig를 기반으로 생성하기 위해 해당 클래스를 파라미터로 넘긴다.

import hello.core.Order.OrderService;
import hello.core.Order.OrderServiceImpl;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 설정 정보를 변경하는 클래스에 붙이는 애노테이션
public class AppConfig {

    @Bean
    public MemberService memberService() {
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService() {
        System.out.println("call AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());
//        return null;
    }

    @Bean
    public MemberRepository memberRepository() {
//        System.out.println("call AppConfig.memberRepository");
        return new MemoryMemberRepository(); // 저장소가 변경되면 여기만 변경하면 된다
    }

    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy(); // 할인 정책 변경시 여기만 변경하면 된다
//        return new FixDiscountPolicy();
    }

}

 

위와 같은 정보로 컨테이너를 생성하면 컨테이너의 구성은 다음과 같게 되고, 각 빈들끼리 의존관계가 완성되게 된다.

이때 의존 관계를 서로 주입 하며 관계를 완성하는 것을 DI (Dependency Injection) 의존성 주입 이라고 한다.


Bean 조회 하기

컨테이너에 등록된 Bean은 다음과 같은 방법으로 조회할 수 있다.

가장 쉬운 방법은 ac.getBean() 메서드를 사용하는 것이다.

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.DisplayName;
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.*;

public class ApplicationContextBasicFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 이름으로 조회")
    void findBeanByName() {
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("이름 없이 타입으로만 조회")
    void findBeanByType() {
        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    // 아래 테스트는 좋은 코드는 아님 -> DIP 구체화에 의존 하지 않고 있다 -> 다만 언젠가 쓸 수도 있다 -> 구체화에 의존 하지말자
    @Test
    @DisplayName("구체 타입으로 조회")
    void findBeanByName2() {
        MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("빈 이름으로 조회X")
    void findBeanByNameX() {
        MemberService xxxx = ac.getBean("xxxx", MemberService.class);
        assertThrows(NoSuchBeanDefinitionException.class,
                () -> ac.getBean("xxxx", MemberService.class)); // 예외가 터져야 테스트 성공
    }
}

테스트 결과 모든 테스트가 정상적으로 수행되었다. 

그런데, 만약 타입으로 빈을 조회하는 과정에서 동일한 빈이 두개 이상이라면 어떻게 될까?


동일 타입 Bean이 2개 이상일 때

정답은 오류가 발생한다 ! 이다.

이 오류를 해소하기 위해선 각 빈에 고유한 이름을 지정해주어야 한다. 참고로 컨테이너의 등록된 빈 이름은 절대 중복되어선 안된다.

다음 예시를 보자.

static class SameBeanConfig {

        @Bean
        public MemberRepository memberRepository1() {
            return new MemoryMemberRepository();
        }

        @Bean
        public MemberRepository memberRepository2() {
            return new MemoryMemberRepository();
        }
    }

MemberRepository 라는 빈이 두개 등록 되었다.

이때는 타입으로 단순히 조회하면 다음과 같이 NoUniqueBeanDefinition 예외가 발생한다.

 

이때는 다음과 같이 해소하면 된다.

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByName() {
	MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
	assertThat(memberRepository).isInstanceOf(MemberRepository.class);

 


스프링 빈의 상속 관계

부모타입으로 빈을 조회하면, 자식 타입까지 모두 조회 된다.

그래서 Object타입 조회시 모든 스프링빈을 조회 하게 된다.

'Spring > Core' 카테고리의 다른 글

[Spring] @ComponentScan , @AutoWired - 자동 Bean 등록  (0) 2025.06.16
[Spring] Singleton 패턴 : 인스턴스 아나바다 운동  (0) 2025.06.16
[Spring] BeanFactory와 ApplicationContext  (0) 2025.05.25
[Spring] @PostConstruct, @PreDestroy 와 컴포넌트 스캔의 궁합  (1) 2025.05.19
[Spring] Bean 생명주기와 콜백  (0) 2025.05.19
'Spring/Core' 카테고리의 다른 글
  • [Spring] @ComponentScan , @AutoWired - 자동 Bean 등록
  • [Spring] Singleton 패턴 : 인스턴스 아나바다 운동
  • [Spring] BeanFactory와 ApplicationContext
  • [Spring] @PostConstruct, @PreDestroy 와 컴포넌트 스캔의 궁합
xuv2
xuv2
기록하는 습관
  • xuv2
    xuvlog
    xuv2
  • 전체
    오늘
    어제
    • 전체 글 모아보기 (128) N
      • 잡담 (9)
      • 도전 , 자격증 (2)
      • Error (4) N
      • Java (23) N
      • Spring (7) N
        • Core (6) N
        • MVC (1)
      • DataBase (6)
        • Database Modeling (4)
        • SQL (2)
      • HTTP (11)
      • Network (17)
      • Software Engineering (3)
      • Operating System (3)
      • Algorithm (16)
      • Project (9)
        • Web (0)
        • iOS (8)
        • Python (1)
      • A.I (13)
      • Linux (5)
  • 블로그 메뉴

    • 홈
  • 링크

    • Github
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
xuv2
[Spring] 스프링 컨테이너 - ApplicationContext, Bean 조회
상단으로

티스토리툴바