
@Configuration 이란?
- 스프링 설정 등록을 위한 클래스에 붙이는 어노테이션
@Configuration과 싱글톤
- AppConfig를 살펴보면 memberRepository() 메소드가 2번 호출되어 인스턴스가 2개 생성되므로 싱글톤이 깨지는 것으로 보임
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
- 그러나 Test를 확인해보면 메소드도 한번만 호출되고 인스턴스도 하나만 생성되어 싱글톤이 지켜짐
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository " + memberRepository1);
System.out.println("orderService -> memberRepository " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}

@Configuration과 바이트코드 조작
- 자바 코드로만 보면 인스턴스 2개가 생성되는 것이 맞지만 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용하여 하나만 생성되도록 한다
- Test
@Test
void configurationDeppTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}

- 순수한 클래스라면 class hello.core.AppConfig로 나와야 하지만 xxxCGLIB가 붙어있음
- 이것은 내가 만든 클래스가 아니라 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다
- @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.
- 이를 통해 싱글톤을 유지할 수 있다
@Configuration이 없다면?
- 메소드에 @Bean만 붙어있어도 스프링컨테이너에 빈으로 등록이 됨
- Test
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository " + memberRepository1);
System.out.println("orderService -> memberRepository " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}

- 하지만 @Configuration이 없기 때문에 싱글톤이 유지되지 않음