1. 하나의 프로젝트 당 컨테이너는 하나만 생성된다
아래 사진과 같은 테스트 코드를 짜서 실행해 보았는데 correct가 떳다.. 아닛?
코드 설명을 하자면 두 개의 @configuration 이 있고 ac 컨테이너를 memberA를 memberService 에 등록을 했다. 게다가 다른 컨테이너인 ac2를 만들었는다!
만약 컨테이너가 따로 만들어 졌다면 ac2컨테이너에 등록 되어 있는 memberService2에서 memberA가 조회되면 안된다. 반대로 스프링 컨테이너는 단지 하나고 AnnotationConfigApplicationContext는 그 하나의 컨테이너에 스프링 빈들을 등록해주는 하나의 수단일 뿐이라서 memberA가 조회가 가능해진다.
근데 가능 했다! 즉, 그러하니 AnnotationConfigApplicationContext이 함수는 컨테이너 생성 함수라고 생각했는데 엄연히 컨테이너 등록 함수임이 밝혀졌다. 오해해서 미안~
2. AppConfig 자체도 컨테이너에 등록 된다.
이게 무슨 뜻이냐면 @Configuration 어노테이션을 붙여 의존관계 주입에 힘을 써주는 AppConfig 자체도 스프링 빈으로 등록 된다는 뜻이다! @Bean을 안붙였는데도 등록된다. 아래 사진 참고!
게다가 자동 의존관계 주입인 @ComponentScan을 붙여 자동 의존관계 주입을 한 클래스도 스프링 빈으로 등록 된다.
하지만 사진에서 보이듯이 CGLIB가 붙어있다. 이 말인 뜻은 내가 만든 appConfig가 등록 된 것이 아닌, 스프링이 바이트 코드 조작 라이브러리를 통해 변경 했다는 의미이다. 이 짓을 왜 하냐면 싱글톤으로 관리되는 빈들은 단 한 번만 생성이 되어야만 하므로 생성 할때마다 "어 이거 존재해? 그럼 가져다 써야지~ 안만들꺼얌" 이런 확인 절차를 거치게 하기 위해서이다. 그럼으로서 싱글톤 특성이 유지가 될 수 있다.
참고로 CGLIB가 붙으면 원본의 자식 타입!!
3. 자동 빈은 인스턴스, 수동 빈은 인터페이스의 이름이 컨테이너에 등록된다.
이건 사실 그렇게 중요하진 않다. ㅎㅎ 어차피 수동 빈 등록을 해도 인스턴스를 찾아서 넣고 이때 충돌이 일어나지 않게 getBean 인자에 함수 명과 리턴 타입 둘 다 넣어 준다.
아무튼 둘 다 생성자 주입을 했다고 하면 스프링 실행시 console창에 컨테이너에 등록 된 이름들이 보이는데 아래 두 사진과 같았다.
4. 생성자의 파라미터에 Map이나 List에 의존관계 주입이 일어나면 알아서 값이 push가 됨
이건 아래 코드를 보면 된다.
public class AllBeanTest {
@Test
void findAllBean(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice = discountService.discount(member, 200000, "fixDiscountPolicy");
assertThat(discountService).isInstanceOf(DiscountService.class);
assertThat(discountPrice).isEqualTo(1000);
}
static class DiscountService{
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
//@Autowired
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = " + policyMap + ", policies = " + policies);
}
public int discount(Member member, int price, String discountCode){
DiscountPolicy discountPolicy = policyMap.get(discountCode);
System.out.println("discountCode = " + discountCode);
System.out.println("discountPolicy = " + discountPolicy);
return discountPolicy.discount(member,price);
}
}
}
@Autowired 는 어차피 생성자 하나밖에 없으니 생략 가능해서 주석 처리해봤당.
내가 이 코드에서 너무나 의아했던 것은 policyMap에 어떠한 push연산을 하지 않았고, 단지 new Annotation 뭐시기에 등록만 했는데도 불구하고 DiscountPolicy 인터페이스를 상속 받은 모든 인스턴스들의 <빈 이름, 빈> 쌍들이 push 되는 것이다. List도 마찬가지. 당연히 의존관계 주입을 해주는 함수에서 그 값이 쓰여야만이 가능한 일이다. 상당히 편리한 기능!
정리
지금 껏 배운 스프링 공부를 정리해 보았는데 확실히 혼자서 코딩 해보고 고민하면서 부딪히니 얻어가는 것도 많고 머리 속에 더 잘 정리 된 것 같다. 이젠 다시 또 다음 파트를 공부하면서 블로그에 착실하게 정리해야지이~!
'백앤드(스프링)' 카테고리의 다른 글
HTTP 총 정리 2편 (0) | 2022.02.24 |
---|---|
HTTP 총 정리 1편 (0) | 2022.02.23 |
빈 스코프 2편 (0) | 2022.01.28 |
빈 생명주기, 빈 스코프 1편 (0) | 2022.01.27 |
의존관계 자동주입 2편 (0) | 2022.01.25 |