컨테이너에 등록된 모든 빈 조회
빈 이름으로 조회할 경우
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MemberService memberService = ac.getBean("memberService", MemberService.class); //MemberService, 즉 인터페이스로 조회함 그러면 이 인터페이스의 구현체가 대상이 된다
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
//isInstanceOf() 연산자:해당 타입의 인스턴스인지를 비교하는 메서드, 객체가 특정클래스나 인터페이스로부터 생성된 것인지를 판별
}
멤버서비스가 멤버 서비스 인플의 인스턴스면 성공
구체 타입으로 조회할 경우
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class); //스프링 컨테이너에 MemberServiceImpl이 등록되어있으면 조회가 됨
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Configuration
public class AppConfig { //나의 애플리케이션 전체에 대해 설정하고 구성하는 공간
//멤버서비스에 대한 구현을 나의 애플리케이션에서는 멤버서비스 인플을 쓸거야
@Bean
public MemberService memberService() { //AppCofig를 쓸때 멤버서비스 씀
return new MemberServiceImpl(MemberRepository()); //멤버서비스 구현체인 객체가 생성됨
}
반환타입 MemberService이 아니라 MemberServiceImpl해도 상관없다.
스프링빈에 등록된 인스턴스 타입을 보고 결정하기 때문에 꼭 여기있는 인터페이스( MemberService) 아니어도 된다. MemberServiceImpl 해도 된다.
/**
* 구체적인건 좋지 않다
* 역할과 구현을 구분해야한다. 항상 역할에 의존해야한다.
* 근데 이건 구현에 의존한게됨
*/
빈 이름으로 조회할 경우
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanNameX() { //항상 성공테스트뿐만 아니라 실패 테스트도 만들어야함
MemberService xxxx = ac.getBean("xxxxx", MemberService.class);
}

NoSuchBeanDefinitionException : No bean named 'xxxxx' available 에러가 뜬다.
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanNameX() { //항상 성공테스트뿐만 아니라 실패 테스트도 만들어야함
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxxx", MemberService.class)); //무조건 이 예외가 터져야 성공한다는 뜻 static import : 알트+엔터
}
() -> ac.getBean("xxxxx", MemberService.class)이 실행되면 NoSuchBeanDefinitionException.class 예외가 실행되어야 한다. 이 예외가 터지면 성공, 아니면 실패
동일한 타입이 둘 이상
class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByTypeDuplicate() {
MemberRepository bean = ac.getBean(MemberRepository.class); //타입만 지정한 경우 예외 생김
}
@Configuration
static class SameBeanConfig { //테스트하려고 하는거니까 AppConfig를 손대지 않고 안에서만 쓰기 위해 SameBeanConfig를 만듦
//클래스 안에서 static을 쓴다는건 이 안에서만 쓰겠다는 뜻
@Bean //빈의 이름이 다르고 인스턴스 타입이 같을 수 있다
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}

NoUniqueBeanDefinitionException 유니크 하지 않다, 즉 중복이 있다는 예외가 발생함
스프링 빈 조회 - 상속 관계
1.반환타입이 DiscountPolicy 일 경우
@Bean
public DiscountPolicy rateDiscountPoilicy(){
return new RateDiscountPolicy();
}
2.반환타입이 RateDiscountPolicy 일 경우
@Bean
public RateDiscountPolicy rateDiscountPoilicy(){
return new RateDiscountPolicy();
}
DiscountPolicy, RateDiscountPolicy 모두 가능하지만 역할과 구현을 나누는 측면에서 1번이 좋다.
반환타입을 보고 역할을 알 수 있다. DI하고 있을때도 활용하기 쉽게 1번이 좋다.
@Test
@DisplayName("부모 타입으로 모두 조회하기")
void findAllBeanByParentType() {
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key+"value = "+beansOfType.get(key));
}
}
테스트 할때 println 안하는게 좋다. 자동 통과/실패를 시스템이 결정하도록 해야한다.
테스트 디버깅할때는 괜찮다
@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));
}
}

스프링 여러가지 빈들이 등록 된게 다 나온다. 왜냐하면 Java 객체는 모든게 Object 타입이기 때문이다
실제로 getBean을 쓸 일은 없지만 기본기능이고 순수한 Java 어플리케이션에서 스프링컨테이너를 생성해서 써야할 일이 있다.
BeanFactory와 ApplicationContext
환경변수: 로컬, 개발, 운영 등을 구분해서 처리
로컬 환경:처음 내 PC에서 개발한 환경
개발 환경:여러 시스템을 엮어서 실제 테스트 서버에 띄워두고 테스트할 수 있는 테스트 서버
운영 환경: 실제 프로덕션에 나가는 운영환경
스테이징 환경:운영과 가장 비슷한 밀접한 환경
각 환경별로 어떤 데이터베이스에 연결해야할 지, 그런 환경변수와 관련된 정보를 처리해주는 기능을 제공함
편리한 리소스 조회: 파일을 읽어서 내부에서 쓸 때 추상화해서 편리하게 쓸 수 있는 기능 제공
스프링 빈 설정 메타 정보 - BeanDefinition
BeanDefinition에 빈의 메타정보를 담는다
appConfig를 가지고 BeanDefinition 설정정보를 만든다 bin 당 하나의 설정정보다
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("beanDefinition = " + beanDefinition +
"beanDefinition = "+ beanDefinition);
}
}
}
}

출력 결과가 에서 scope 가 할당이 안되어 있으면 싱글톤
abstract, lazy : 보통 스프링 빈들이 스프링 컨테이너가 뜰 때 다 등록이 되는데 그게 아니라 실제 사용하는 시점에 나중에 실제 사용하는 시점에 스프링빈을 초기화해 라는 정보
factoryBeanName=appConfig; factoryMethodName=memberService;
appConfig에 있는 memberService를 호출해서 실제 빈을 생성할 수 있구나 라는 뜻
이런 정보들이 있다는 정도만 이해
이런 메타정보를 기반으로 인스턴스를 생성함
스프링 빈을 만들때는 1. 직접적으로 스프링 빈을 등록하는 방법 2. 팩토리 빈으로 등록하는 방식(Java config)