@Configuration 을 붙이면 생성된 빈을 싱글톤으로 관리해주고 클래스 내에서 객체 생성 호출 시 싱글톤을 보장해준다.
그러나 붙이지 않는다면 생성된 빈을 싱글톤으로 관리는 해주지만 클래스 내에서 객체 생성 호출 시 싱글톤을 보장해주지는 않는다.
아래의 예시 코드로 정리해보았다.
먼저 클래스 내에서 객체 생성 호출 시 싱글톤을 보장해주지 않는 예시 코드다.
// 설정 정보를 읽어서 @Bean이 붙은 메서드 목록을 저장한다.
// 메서드 목록을 하나씩 실행하며 반환되는 객체를 빈으로 등록한다.
public class AppConfig {
@Bean
public MemberService memberService() {
// MemberServiceImpl 객체는 스프링 빈으로 등록되지만,
// 의존 관계로 주입되는 객체인 memberRepository()의 반환값인
// MemoryMemberRepository 객체는 빈으로 등록되지 않는다.
return new MemberServiceImpl(memberRepository());
}
@Bean MemberService memberService2() {
// 이미 스프링 빈으로 등록된 MemberServiceImpl 객체를 참조하지 않고
// 새로운 객체를 만들어서 스프링 빈을 등록한다.
return memberService();
}
@Bean
public OrderService orderService() {
// 첫번째 경우와 마찬가지로 의존 관계 주입인 객체들을 제외하고
// OrderServiceImpl 객체만 빈으로 등록된다.
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
// MemoryMemberRepository 객체는 비로소 여기서 빈으로 등록된다.
return new MemoryMemberRepository();
}
...
}
일단 의존 관계로 주입되는 객체는 빈으로 등록하지 않고, 단순히 새로운 일반 객체로 생성해서 사용할 뿐이다.
그리고 의존 관계가 아닌, 같은 객체를 반환하는 경우이더라도 @Configuration이 없으면 클래스 내에서 객체 생성 호출 시 싱글톤을 보장해주지 않기 때문에 새로운 스프링 빈을 등록하게 된다.
즉 클래스 내에서 객체 생성 호출 시 객체들이 같은 인스턴스를 참조하는게 아니라 매번 다른 인스턴스를 참조하기 때문에 클래스 내에서 객체 생성 호출 시 싱글톤 보장이 되지 않는다고 표현한다.
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
MemberServiceImpl memberService2 = ac.getBean("memberService2", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
//모두 다른 인스턴스를 참고하고 있다.
System.out.println(memberService.getMemberRepository());
System.out.println(orderService.getMemberRepository());
System.out.println(memberRepository);
//서로 다른 인스턴스를 참고하고 있다.
System.out.println(memberService);
System.out.println(memberService2);
}
}
@Configuration을 붙이지 않았지만 컨테이너에 빈 등록 시, AnnotationConfigApplicationContext 파라미터로 AppConfig를 넘기면 앞서 언급한 AppConfig 과정이 실행되어 기본값인 싱글톤 스코프 빈으로 컨테이너에 등록된다.
아까 위에서의 클래스 내에서 객체 생성 호출 시 싱글톤을 보장해주지 않는 경우는 정확히는 이 코드에서 출력값을 통해 알 수 있다.
첫 3개의 출력값은 모두 다르게 나온다. memberRepository만 스프링 빈으로 등록된 주소가 출력되고, 나머지 둘은 일반 객체로 생성된 주소가 출력된다.
다음 2개의 출력값 역시 서로 다르게 나온다. 둘 모두 스프링 빈으로 등록된 주소지만 서로 다르게 빈으로 등록되었기 때문에 다른 주소값이 출력된다.
다음으로 생성된 빈이 싱글톤으로 관리됨을 보여주는 예시 코드다.
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService1 = ac.getBean("memberService", MemberServiceImpl.class);
MemberServiceImpl memberService2 = ac.getBean("memberService", MemberServiceImpl.class);
//서로 같은 인스턴스를 참고하고 있다.
System.out.println(memberService1.getMemberRepository());
System.out.println(memberService2.getMemberRepository());
}
}
같은 빈을 여러 번 조회하게 된다면, 그 빈 자체는 하나의 인스턴스로만 생성되고 관리되기 때문에 언제든지 같은 값이 조회된다. 위 출력값도 서로 같은 값이 나오며, 조회를 더 많은 횟수로 하더라도 계속 같은 값이 나온다. 이와 같이 생성된 빈은 싱글톤으로 관리가 된다.
정리를 하자면, @Configuration을 붙이지 않는다면 스프링 빈을 등록하는 과정에서의 클래스 내 객체 생성 호출은 싱글톤을 보장하지 않으며 이후 생성된 빈에 대해서는 싱글톤으로 관리된다.
참고 출처 :