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