ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
이전에 만들었던 전체 애플리케이션의 실행 동작을 담당하는 설정 클래스인 AppConfig 클래스를 기반으로 스프링 컨테이너를 만든다.
ApplicationContext
는 인터페이스고 new AnnotationConfigApplicationContext(AppConfig.class)
는 인터페이스의 구현체이다.
✔️1. 스프링 컨테이너 생성
new AnnotationConfigApplicationContext(AppConfig.class);
스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다.
여기서는 AppConfig.class
를 구성 정보로 지정했다.
✔️2. 스프링 빈 등록
스프링 빈 이름 : 메서드 이름
기본적으로 지정되는 메서드 이름 대신 빈 이름을 직접 부여할 수 있다.
✔️3. 스프링 빈 의존관계 설정 - 준비
스프링 컨테이너 내에 객체를 생성한다.
✔️4. 스프링 빈 의존관계 설정 - 완료
스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입한다.
public class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
@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(beanDefinitionName + " : " + bean);
} else if (beanDefinition.getRole() == BeanDefinition.ROLE_INFRASTRUCTURE) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println(beanDefinitionName + " : " + bean);
}
}
}
}
✔️모든 빈 출력하기
ac.getBeanDefinitionNames()
: 스프링에 등록된 모든 빈 이름을 조회한다.ac.getBean()
: 빈 이름으로 빈 객체를 조회한다.✔️애플리케이션 빈 출력하기
getRole()
로 구분할 수 있다. ROLE_APPLICATION
ROLE_INFRASTRUCTURE
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MemberService memberService = ac.getBean("memberService", MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
@Test
@DisplayName("빈 타입으로 조회")
void findBeanByType() {
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX() {
// MemberService xxxxx = ac.getBean("xxxxx", MemberService.class);
assertThrows(NoSuchBeanDefinitionException.class, () -> ac.getBean("xxxxx", MemberService.class));
}
}
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면 중복 오류가 발생")
void findBeanByTypeDuplicate() {
//MemberRepository memberRepository = ac.getBean(MemberRepository.class);❌️
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(MemberRepository.class));
}
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}
기존 AppConfig
의 수정 대신 새로운 정적 중첩 클래스 SameBeanConfig
를 만든 후 MemberRepository
스프링 빈을 등록한다. 테스트를 실행해보면 아래와 같은 에러가 발생한다.
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.member.MemberRepository' available: expected single matching bean but found 2: memberRepository1,memberRepository2
위와 같이 동일한 타입의 빈이 둘 이상 있으면 중복 오류가 발생하기에 타입으로 조회시 같은 타입의 빈이 둘 이상 있다면 빈 이름을 지정해야한다.
또한 특정 타입을 getBeansOfType()
메서드를 사용해서 모두 조회할 수 있다.
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면 중복 오류가 발생")
void findBeanByTypeDuplicate() {
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(MemberRepository.class));
}
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면 빈 이름을 지정하면 된다.")
void findByName() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeansByType() {
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for (String s : beansOfType.keySet()) {
System.out.println("key = " + s + ", value = " + beansOfType.get(s));
}
}
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}
✔️부모 타입의 빈으로 조회하면 자식 타입도 함께 조회된다.
public class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시 자식이 둘 이상 있으면 중복 오류가 발생한다.")
void findBeanByParentTypeDuplicate() {
//DiscountPolicy bean = ac.getBean(DiscountPolicy.class);❌️
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회시 자식이 둘 이상 있으면 빈 이름을 지정하면 된다.")
void findBeanByParentTypeBeanName() {
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(DiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 모두 조회하기 - 자식 타입도 함께 조회가 된다.")
void findAllBeanByParentType() {
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
for (String s : beansOfType.keySet()) {
System.out.println("key = " + s + ", value = " + beansOfType.get(s));
}
}
@Test
@DisplayName("Object 타입으로 모두 조회하기")
void findAllBeanByObjectType() {
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
for (String s : beansOfType.keySet()) {
System.out.println("key = " + s + ", value = " + beansOfType.get(s));
}
}
@Configuration
static class TestConfig {
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
}
✔️인터페이스의 상속
DiscountPolicy
는 인터페이스이고,RateDiscountPolicy
나FixDiscountPolicy
를DiscountPolicy
의 구현체라고 한다. 다형성과 계층구조 관점에서 보면DiscountPolicy
는RateDiscountPolicy
나FixDiscountPolicy
의 부모로 볼 수 있다.
✔️BeanFactory
getBean()
을 제공✔️ApplicationContext
✔️어노테이션 기반 자바 코드 설정 사용
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
✔️XML 설정 사용(잘 사용하지 않는 추세)
public class XmlAppContext {
@Test
void xmlAppContext() {
ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
MemberService memberService = ac.getBean("memberService", MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
}
<?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="hello.core.member.MemberServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
</bean>
<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>
<bean id="orderService" class="hello.core.order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
<constructor-arg name="discountPolicy" ref="disCountPolicy"/>
</bean>
<bean id="disCountPolicy" class="hello.core.discount.RateDiscountPolicy"/>
</beans>
public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 설정 메타 정보 확인")
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);
}
}
}
}
public class BeanDefinitionTest {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 설정 메타 정보 확인")
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);
}
}
}
}