월루를 꿈꾸는 대학생
[핵심원리-기본] 스프링 컨테이너와 스프링빈 본문
스프링 컨테이너 생성
ApplicationContext applicationContext =
new
AnnotationConfigApplicationContext(AppConfig.class); // 해당 구문이 ApplicationContxt의 구현체 다형성
- ApplicationContext = 스프링 컨테이너 / 인터페이스 -> 다형성
- 애노테이션 기반의 자바 설정 클래스로 만들어서 사용
- AppConfig 방식이 애노테이션 기반 자바 설정 클래스로 컨테이너 만든것
일반적으로 ApplicationContext를 스프링 컨테이너라고 한다
스프링 컨테이너 생성 과정
- new AnnotationConfigApplicationContext(AppConfig.class)
- 스프링 컨테이너를 생성할 때는 구성 정보를 지정 키-값
- AppConfig.class 를 구성 정보로 지정
- 구성정보로 지정한 클래스를 기준으로 스프링 빈 저장소에다가 객체 생성에서 집어넣음
- 파라미터로 넘어온 클래스 정보를 사용해서 스프링 빈 정보를 등록
빈 이름
- 빈 이름은 메서드 이름으로 사용 일반적
- 빈 이름 직접 지정도 가능은 함
@Bean(name="memberService2")
빈 이름은 항상 다른 이름 부여를 해야함
- 설정 정보를 참고해서 의존관계를 주입 DI
- 동적인 의존관계를 스프링이 알아서 만들어줌
컨테이너 등록된 모든 빈 조회
- 제대로 빈이 등록되었는지 확인
테스트 코드 작성해서 확인
ac.getBeanDefinitionNames() -> 등록된 모든 빈 이름을 조회
ac.getBean() -> 빈의 이름으로 객체를 조회
public class AppliocationContextTest { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); @Test @DisplayName("모든 빈을 출력하기") void findAllBean(){ String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { Object bean = ac.getBean(beanDefinitionName); System.out.println("name : " + 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) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name : " + beanDefinitionName + " object : " + bean);
}
}
}
내가 사용한 빈들만 나오도록 설정
출력결과
스프링 빈 조회 - 기본
- ac.getBean(빈이름 , 타입)
- ac.getBean(타입)
빈이름으로 조회
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName(){
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
// 해당 객체는 빈에 등록된 객체이고 해당 객체의 인스턴스는 MemberServiceImpl 와 동일하다
}
타입으로만 조회
해당 타입의 조회는 인터페이스를 넣어서 getBean실행 -> 다형성
@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); }
만약 이름이 존재하지 않는다면 ?
- 없는 이름으로 빈 조회시 NoSuchBeanDefinitionException 에러 발생
- 이런 테스트는 해당 에러를 나오도록 테스트해서 검증
- org.junit.jupiter.api.Assertions.assertThrows 사용
위의 에러가 나오니까 이를 테스트
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX(){
assertThrows(NoSuchBeanDefinitionException.class,
()->ac.getBean("xxxx", MemberServiceImpl.class));
}
스프링 빈 조회 - 동일한 타입이 둘 이상
- 같은 타입이 여러개 있으면 뭘 줘야할지 판단 불가 - 오류 발생
- 빈 이름으로 조회하자
- ac.getBeanOfType() 사용시 해당 타입의 모든 빈 조회가능
예외가 터진다
NoUniqueBeanDefinitionException
일단 테스트용 구성파일을 클래스안에다가 구현
@Configuration // 클래스 안에 static으로 클래스 정의한 건 스코프를 해당 클래스 내로 한정한다는 소리
static class SameBeanConfig{
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2(){
return new MemoryMemberRepository();
}
}
타입이 둘 이상 있는 경우 위에 같은 에러가 나오는데 이를 핸들링한 테스트 코드
@Test @DisplayName("타입으로 조회시 같은 타입이 둘 잇아있으면 중복오류가 발생한다") void findBeanByTypeDuplicate(){ // 타입만 지정함 //MemberRepository bean = ac.getBean(MemberRepository.class); assertThrows(NoUniqueBeanDefinitionException.class, ()-> ac.getBean(MemberRepository.class)); }
빈 이름을 통해서 조회한다면 에러 없이 조회 가능
@Test @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면 빈 이름을 지정하면 된다 ") void findBeanByName(){ MemberRepository bean = ac.getBean("memberRepository", MemberRepository.class); assertThat(bean).isInstanceOf(MemberRepository.class); }
그냥 특정 타입들을 조회하는 경우
- ac.getBeansOfType(MemberRepository.class) 를 사용하여 해당 인터페이스를 기준으로 빈 조회하기
스프링 빈 조회 - 상속관계
부모타입으로 조회시 자식까지 같이 조회됨!!
Object로 조회하면 모든 스프링 빈 조회
부모타입 조회할 때 자식이 2개 이상이면 또 오류남
NoUniqueBeanDefinitionException 에러
@Test @DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다") void findBeanByParentTypeDuplicate() { //DiscountPolicy bean = ac.getBean(DiscountPolicy.class); assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(DiscountPolicy.class)); }
그냥 조회할 때마다 빈이 2개 이상이면 에러가 나는 듯 그런 경우 이름으로 조회하면 오케
보통 빈을 조회하는 경우는 극히 드물다
BeanFactory와 ApplicationContext
BeanFactory
- 스프링 컨테이너 최상위 인터페이스
- 스프링 빈 관리 및 조회 getBean() 같은거
- 대부분 썼던 기능은 BeanFactory꺼
ApplicaionCOntext
BeanFactory 상속받아서 제공
그 외 부가 기능을 담당
메시지소스를 활용한 국제화 기능
- 예를 들어서 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력
환경변수
- 로컬, 개발, 운영등을 구분해서 처리
애플리케이션 이벤트
- 이벤트를 발행하고 구독하는 모델을 편리하게 지원
편리한 리소스 조회
- 파일, 클래스패스, 외부 등에서 리소스를 편리하게 조회
BeanFactory 잘 안 씀 ApplicationContext = 스프링 컨테이너
다양한 설정 형식 지원 - 자바 코드 , XML
- 스프링 컨테이너는 다양한 형식의 설정정보를 지원!
애노테이션 기반 자바 코드
- new AnnotationConfigApplicationContext(AppConfig.class)
- 지금까지 썼던 거
XML 설정 사용
- 요즘 안 씀
- xml 사용하면 컴파일 없이 빈 설정 정보 변경
- GenericXmlApplicationContex를 써서 xml설정파일 넘겨주면 오케
스프링 빈 설정 메타 정보 - BeanDefinition
- 스프링은 어떻게 xml, 자바 코드를 다 지우너할 수 있을까 ?? -> 빈정보 자체를 추상화해서 그런거임 BeanDefinition 로 추상화해서 가능
- 즉 역할과 구현으로 개념적 나눈것
- 자바 코드나 xml이나 설정 정보들을 BeanDefinition으로 만들고 넘겨주니까 스프링은 이게 xml이든 자바코드든 신경 안써도 되는 거임
- BeanDefinition = 빈 설정 메타정보
- ==@Bean== , ==< bean >==이 각각 하나의 메타 정보로 생성 됨
스프링 컨테이너는 메타정보를 가지고 스프링 빈을 생성한다
BeanDefination 이 인터페이스임
스프링 컨테이너는 추상화에만 의존하는 것
==AnnotationConfigApplicationContext== 는 ==AnnotatedBeanDefinitionReader== 를 사용해서
AppConfig.class 를 읽고 BeanDefinition 을 생성
즉 어떤거로 해동 BeanDefition을 만들어 버리니까 멀로 만들어도 괜찮
public class BeanDefinitionTest {
// 여기서 추상화 안하고 AnnotationConfigApplicationContext 쓴 이유는 ac.getBeanDefinitionNames 이게 정의가 되어있지 않음
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("beanDefinationName = " + beanDefinitionName +
"beandefination : " + beanDefinition);
}
}
}
}
다음과 같이 해당 AnnotationConfigApplicationContext을 가지고 온 다음에 어떤 BeanDefinition이 있는지 확인하는 테스트
출력 결과
여기 있는 출력결과에서 BeanDefinition의 메타정보를 확인 가능
이 메타정보를 가지고 인스턴스 생성
실무에서 BeanDefinition을 정의 사용하지는 않는다
다만 추상화해서 사용하는 것만 이해하는거로 오케
'Programing > Spring Boot' 카테고리의 다른 글
[핵심원리-기본] 컴포넌트 스캔 (0) | 2023.07.18 |
---|---|
[핵심원리-기본] 싱글톤 컨테이너 (0) | 2023.07.18 |
[핵심원리-기본] 객체지향 원리 적용 (0) | 2023.07.18 |
[핵심 원리-기본]비즈니스 요구사항과 설계 (0) | 2023.07.18 |
[스프링 핵심 원리 기본] 객체 지향 설계와 스프링 (0) | 2023.07.07 |