스프링 컨테이너의 환경설정을 다룬다. @Configuration
package hello.core;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import hello.core.discount.Discountpolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
@Configuration
public class AppConfig {
private int number = 0;
@Bean
public MemberService memberService() {
number ++;
System.out.println("call memberService");
System.out.println(number);
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
number ++;
System.out.println("call memberRepository");
System.out.println(number);
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
number ++;
System.out.println("call orderService");
System.out.println(number);
return new OrderServiceImpl(memberRepository(), discountpolicy());
}
@Bean
public Discountpolicy discountpolicy() {
// return new RateDiscountPolicy();
return new FixDiscountPolicy();
}
}
위의 소스에서 주목해야할 점
MemberServiceImpl, OrderServiceImpl, MemberRepository 객체는
각각 return new MemoryMemberRepository()를 리턴하고 있다.
허나 MemberRepository는 단 한 번만 호출되는데
즉 new MemoryMemberRepository()가 1번만 생성되는것이고
싱글톤을 유지한다는 점.
비밀은 스프링의 @Configuration 어노테이션..
@Test
public void AppConfigTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
System.out.println("AppConfig 클래스 네임 : "+ bean.getClass()); // class hello.core.AppConfig$$EnhancerBySpringCGLIB$$989baddf
System.out.println("memberService 클래스 네임 : "+ memberService.getClass()); // class hello.core.member.MemberServiceImpl
}
위의 예제를 돌리면
Appconfig 빈의 클래스명 뒤에
'$$EnhancerBySpringCGLIB$$989baddf' 가 추가된 걸 확인 할 수 있다.
이는 스프링이 CGLIB라이브러리라는 바이트코드 조작 라이브러리를 사용해서 Appconfig 클래스를 상속받은 임의의 다른 클래스를 만들고,
그 다른 클래스를 스프 빈으로 등록한것.
그 임의의 다른 Appconfig 클래스가 싱글톤이 되도록 보장해주는것이다.
@Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면
존재하는 빈을 반환하고, 없으면 새로 생성해서 스프링 빈을 등록 하고 반환한다.
그 덕분에 스프링컨테이너의 빈들은 싱글톤이 보장된다.
Appconfig 빈의 클래스명 뒤의 $$EnhancerBySpringCGLIB~ 는
사라지게 되고,
@@impl 클래스들이 실행할때 마다 새로운 객체를 생성하게되며,
싱글톤은 깨지게 된다.
그래서 스프링 설정 정보는 항상 @Configuration 사용해라.
추가적으로 스프링 빈들은 stateless를 보장해야한다.