티스토리 뷰

728x90
반응형

싱글턴 범위 빈과 싱글턴 범위 의존성은 ApplicationContext 인스턴스가 생성될 때 함께 생성됩니다. 반면 프로토타입 범위 빈과 프로토타입 범위 의존성은 프로토타입 범위 빈을 얻기 위해 ApplicationContext의 getBean 매서드를 호출할 때마다 생성됩니다. 이때 싱글턴 범위의 빈이 프로토타입 범위의 빈을 필요로 한 경우와 그 반대의 경우가 발생할 때 어떻게 해야 할까요?

예제를 보면서 살펴보겠습니다.

 

Case 01 - 싱글턴 범위 빈의 의존성

  • service 빈은 싱글턴 빈과 프로토타입의 빈을 생성자 인자로 가지고 있습니다.
  • 실행 코드에서 service 빈을 가져오게 된다면 details 빈을 생성하지 않아서 예외가 발생하게 됩니다.
  • 스프링 컨테이너는 싱글턴 범위 객체를 한 번만 생성하므로 스프링 컨테이너가 service 빈의 의존성을 주입할 수 있는 기회도 한 번입니다. 따라서 스프링 컨테이너는 프로토타입 범위의 details 빈 인스턴스를 service 빈으로 한 번만 주입하게 됩니다. 이렇게 된다면 service 빈은 수명이 유지되는 동안 동일한 details 빈의 참조를 가지게 되며, 여러 클라이언트가 동시에 service 빈의 인스턴스를 요청하게 된다면 동일한 details 객체의 인스턴스를 반환하게 되고  이는 결제 및 각각의 개인이 처리해야할 프로세스가 있다면 큰 문제점이 발생하게 됩니다.
<bean id="service" class="example.ServiceImpl">
	<constructor-arg name="details" ref="details" />
    <constructor-arg name="dao" ref="dao" />
</bean>

<bean id="details" class="example.Details" scope="prototype" />	<!-- 프로토타입의 빈 -->
<bean id="dao" class="example.Dao" />	<!-- 싱글턴의 빈 -->

 

public class Example {
	public static void main(String args[]) throws Exception {
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");

		Service service = context.getBean(service);
	}
}

 

싱글턴 빈에서 프로토타입 빈의 새 인스턴스 얻기

  • 위의 예제에서 스프링 컨테이너는 싱글턴 범위의 빈을 한 번만 생성하므로 싱글턴 범위 빈의 수명이 유지되는 동안 프로토타입 범위 빈의 동일한 인스턴스를 참조하게 되는 문제가 있었습니다. 이런 경우 어떻게 해결해야 하는지 알아보겠습니다.

<lookup-method> 요소 사용

  • 생명주기가 다른 두 빈에 대한 작업을 할 때 사용합니다.
  • 싱글턴 빈이 프로토타입 빈 참조를 가지고 있어서, 프로토타입의 객체가 싱글턴으로 동작하는 문제를 해결합니다.
  • 테스트 코드에서는 동일한 service 빈 인스턴스가 반환되는게 아닌 각각의 인스턴스가 반환됩니다.

설정 파일

<bean id="service" class="example.CustomerServiceImpl">
	<constructor-arg name="dao" ref="dao" />
	<lookup-method bean="details" name="getCustomerDetails"/>
</bean>

<bean id="dao" class="example.Dao" />
<bean id="details" class="example.Details" scope="prototype" />

CustomerService 인터페이스

public interface CustomerService {
	void submitRequest(String name, String phone);
}

CustomerService 인터페이스를 구현한 CustomerServiceImpl 클래스

public abstract class CustomerServiceImpl implements CustomerService {
	private CustomerDao customerDao;

	@ConstructorProperties({ "customerDao" })
	public CustomerRequestServiceImpl(CustomerDao customerDao) {
		this.customerDao = customerDao;
	}

	public abstract CustomerDetails getCustomerDetails();

	@Override
	public void submitRequest(String name, String phone) {
		CustomerDetails customerDetails = getCustomerDetails();
		customerDetails.setName(name);
		customerDetails.setPhone(phone);
		customerDao.submitRequest(customerDetails);
	}
}

테스트 코드

public class Example {
	public static void main(String args[]) throws Exception {
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");

		CustomerService customerService1 = context.getBean(CustomerService.class);
		customerService1.submitRequest("홍길동", "010-1234-5678");
		customerService1.submitRequest("이순신", "010-1111-2222");
	}
}

 

Case 02 - 프로토타입 범위 빈의 의존성

  • service 빈은 프로토타입의 빈이며, 싱글턴 빈과 프로토타입의 빈을 생성자 인자로 가지고 있습니다.
  • service 인스턴스는 예를 들어 등록 프로세스의 진행 상황을 유지하고 등록 프로세스 중 고객이 제공한 정보를 details 객체에 저장합니다.
  • 아래 예제에서는 ApplicationContext 인스턴스가 생성될 때 dao 빈은 생성이 되고, service빈과 details 빈은 getBean 메서드가 호출될 때마다 생성이 됩니다.
  • 즉 스프링 컨테이너에 프로토타입 범위의 service 빈을 요청하면 스프링 컨테이너는 먼저 service 빈의 의존성인 details 빈의 인스턴스를 생성한 다음 service 빈을 생성하게 됩니다. 즉, 프로토타입 범위의 빈 X가 다른 프로토타입 범위의 빈 Y에 의존하는 경우, 스프링 컨테이너는 빈 X를 요청할 때마다 X와 Y의 새로운 인스턴스를 생성하게 됩니다.
<!-- 프로토타입의 빈 -->
<bean id="service" class="example.ServiceImpl" scope="prototype"
	<constructor-arg name="details" ref="details" />
	<constructor-arg name="dao" ref="dao" />
</bean>

<bean id="details" class="example.Details" scope="prototype" />	<!-- 프로토타입의 빈 -->
<bean id="dao" class="example.Dao" />	<!-- 싱글턴의 빈 -->

 

public class Example {
	public static void main(String args[]) throws Exception {
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");

		Service service1 = context.getBean(service);
		service1.setName("홍길동");
		service1.phone("010-1234-5678");
		service1.add();
        
		Service service2 = context.getBean(service);
	}
}
728x90
반응형