본문 바로가기

Spring

@Configuration이 없을 때의 싱글톤

@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을 붙이지 않는다면 스프링 빈을 등록하는 과정에서의 클래스 내 객체 생성 호출은 싱글톤을 보장하지 않으며 이후 생성된 빈에 대해서는 싱글톤으로 관리된다.

 

 

 

참고 출처 :

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8