김영한 강사님이 제공하시는 인프런 - "스프링 입문 - 스프링 핵심 원리 - 기본편" 강의를 듣고 정리한 내용입니다
강의 링크
김영한 - 스프링 핵심 원리 - 기본편 (유료강의)
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
@Bean(name="memberService2")
⇒ 정리하면
1. 스프링 컨테이너 생성
2. 설정(구성) 정보 참고해서 스프링 빈 등록
3. 의존관계 설정
스프링 컨테이너에 등록한 스프링 빈 확인
src/test/java/hello.core/beanfind/ApplicationContextBasicFindTest.java
class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean() {
// 스프링 컨테이너에 등록된 모든 빈의 이름 조회
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
// 빈 이름(key)로 빈(value) 얻기
Object bean = ac.getBean(beanDefinitionName);
System.out.println("bean = " + beanDefinitionName + " object = " + bean);
}
}
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean() {
// 스프링 컨테이너에 등록된 모든 빈의 이름 조회
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
// 빈에 대한 메타데이터 정보
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
// ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
// ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
// 빈 이름(key)로 빈(value) 얻기
Object bean = ac.getBean(beanDefinitionName);
System.out.println("bean = " + beanDefinitionName + " object = " + bean);
}
}
}
}
모든 빈 출력하기
ac.getBeanDefinitionNames()
: 스프링에 등록된 모든 빈 이름 조회ac.getBean()
: 빈 이름으로 빈 객체(인스턴스) 조회애플리케이션 빈 출력하기
getRole()
로 구분ROLE_APPLICATION
: 일반적으로 사용자가 정의한 빈ROLE_INFRASTRUCTURE
: 스프링이 내부에서 사용하는 빈ac.getBean(빈이름, 타입)
ac.getBean(타입)
src/test/java/hello.core/beanfind/ApplicationContextBasicFindTest.java
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);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
// 실패 테스트
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX() {
// 오른쪽 로직을 실행했을 때 왼쪽 예외가 터져야 테스트 성공
////ac.getBean("xxxxx", MemberService.class);
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxxx", MemberService.class));
}
}
ac.get(타입)
으로 조회할 때, 같은 타입의 스프링 빈이 둘 이상이면 NoUniqueBeanDefinitionException 예외가 발생!
→ ac.get(빈 이름, 타입)
으로 1개의 빈을 조회
→ ac.getBeansOfType()
으로 해당 타입의 모든 빈 조회 가능
src/test/java/hello.core/beanfind/ApplicationContextSameBeanFindTest.java
public class ApplicationContextSameBeanFindTest {
// 스프링 컨테이너 생성
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
// 실패 테스트
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByTypeDuplicate() {
//MemberRepository bean = ac.getBean(MemberRepository.class);
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(MemberRepository.class));
}
// 성공 테스트
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByName() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
// 성공 테스트
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() {
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 = " + beansOfType);
// MemberRepository 타입의 빈이 2개인지 확인
assertThat(beansOfType.size()).isEqualTo(2);
}
// static: ApplicationContextSameBeanFindTest 클래스에서만 쓰겠다
@Configuration
static class SameBeanConfig {
// 빈의 이름이 다르고 객체 인스턴스 타입이 같음(파라미터 값이 다를 수 있음)
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}
getBean(타입)
으로 조회시 같은 타입이 2개 이상이면 NoUniqueBeanDefinitionException 예외가 발생getBean(빈 이름, 타입)
으로 조회하면 성공부모 타입으로 조회하면, 자식 타입도 함께 조회되어 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.
src/test/java/hello.core/beanfind/ApplicationContextExtendsFindTest.java
public class ApplicationContextExtendsFindTest {
// 스프링 컨테이너 생성
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate() {
// DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByParentTypeBeanName() {
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
// 안좋은 방법
@Test
@DisplayName("특정 하위 타입으로 조회")
void findBeanBySubType() {
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 모두 조회")
void findAllBeanByParentType() {
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
// DiscountPolicy 타입인 빈의 정보 출력(빈 이름, 빈 객체)
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
}
// 스프링 컨테이너에 등록된 모든 빈 조회(내부 빈 & 사용자 등록 빈)
@Test
@DisplayName("부모 타입으로 모두 조회하기 - Object")
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 {
// 부모 타입이 DiscountPolicy인 2개의 빈
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
}
getBean(부모 타입)
으로 조회할 때, 자식이 둘 이상이면 NoUniqueBeanDefinitionException 예외 발생getBean(이름, 부모 타입)
으로 조회하면 성공getBean(타입)
으로 특정 하위 타입으로 조회getBeansOfType(부모 타입)
으로 부모 타입+하위 타입의 빈 조회getBeansOfType(Object)
로 모든 빈 조회정리하면
getBeanDefinitionNames()
: ApplicationContext(스프링 컨테이너)에 등록된 모든 스프링 빈 이름 조회getBean(타입)
: ApplicationContext(스프링 컨테이너)에 등록된 해당 타입의 스프링 빈 객체 조회getBean(빈 이름, 타입)
: ApplicationContext(스프링 컨테이너)에 등록된 (빈 이름, 타입)의 스프링 빈 객체 조회getBeansOfType(타입)
: ApplicationContext(스프링 컨테이너)에 등록된 해당 타입의 모든 스프링 빈 객체 조회BeanFactory
ApplicationContext
스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계
애노테이션 기반 자바 코드 설정 사용
new AnnotationConfigApplicationContext(AppConfig.class)
```
AnnotationConfigApplicationContext
클래스를 사용하면서 자바 코드로된 설정 정보를 넘기면 된다.XML 설정 사용
GenericXmlApplicationContext
를 사용하면서 xml 설정 파일을 넘기면 된다 public class XmlAppContext {
@Test
void xmlAppContext() {
// xml 설정파일로 스프링 컨테이너 생성
ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
스프링이 다양한 설정 형식을 지원할 수 있는 이유는?
⇒ BeanDefinition이라는 추상화
⇒ 역할과 구현을 개념적으로 나눈 것
BeanDefinition
@Bean
, <bean>
당 각각 하나씩 메타 정보가 생성BeanDefinition 정보
빈 설정 메타 정보(BeanDefinition)를 확인하는 테스트 코드
public class BeanDefinitionTest {
// getBeanDefintion을 하기 위해 ApplicationContext로 선언하지 않음
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
@Test
@DisplayName("빈 설정 메타정보 확인")
void findApplicationBean() {
// 모든 빈 이름 조회
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
// 빈 이름으로 beanDefintion 조회
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
// 사용자가 등록한 빈의 메타정보만 출력
if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
System.out.println("beanDefinitionName = " + beanDefinitionName +
" beanDefinition = " + beanDefinition);
}
}
}
}