빈 스코프(싱글톤 스코프 & 프로토타입 스코프)

sarah·2022년 9월 2일
0

spring

목록 보기
5/6
post-thumbnail

빈 스코프

  • 싱글톤: 기본 스코프 → 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  • 프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 범위의 스코프
// 컴포넌트 스캔 자동 등록
@Scope("prototype")
@Component
public class Test{}

// 수동 등록(singleton은 기본값, 생략가능)
@Scope("singleton")
@Bean
SingletonBean SingletonBean() {
	return new SingletonBean();
}

프로토타입 스코프

싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈을 반환한다. 반면에 포로토타입 스코프를 스프링 컨테이너에 조회하면 스프링 컨테이너는 항상 새로운 인스턴스를 생성해서 반환한다.

  • 싱글톤 빈 요청

  • 프로토타입 빈 요청

  • 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 관리한다. 클라이언트에 빈을 반환하고, 이후 스프링 컨테이너는 생성된 프로토타입 빈을 관리하지 않는다. 프로토타입 빈을 관리할 책임은 프로토타입 빈을 받은 클라이언트에 있다. 그래서 @PreDestroy 같은 종료 메서드가 호출되지 않는다.

싱글톤 빈의 필드로 프로토타입 스코프인 빈을 주입받는다면?

@Component
public class SingletonBean {
	private final PrototypeBean prototypeBean;
    
    @Autowired
    public SingletonBean(PrototypeBean prototypeBean) {
    	this.prototypeBean = prototypeBean;
    }
    
    public int logic() {
    	prototypeBean.addCount();
        return prototypeBean.getCount();
    }
}

@Scope("prototype");
@Component
public class PrototypeBean {
	private int count;
    
    public int getCount() {
    	return count;
    }
    
    public void addCount() {
    	count++;
    }
}

  • 위와 같이 싱글톤 스코프인 SingletonBean 빈이 프로토타입 스코프인 prototypeBean 빈을 필드로 가진다면, 비록 prototypeBean 빈이 프로토타입이지만 singletonBean 빈의 스코프 단위를 따르게 된다.
  • singtonBean.logic을 실행시킬때마다 prototypeBean의 필드 count는 플러스된다.
  • 아마 원하는 것이 이런 것은 아닐 것이다. 프로토타입 빈을 주입 시점에만 새로 생성하는게 아니라, 사용할 때 마다 새로 생성해서 사용하는 것을 원할 것이다.

프로토타입 스코프 - 싱글톤 빈과 함께 사용시 Provider로 문제 해결

1. ObjectFactory, ObjectProvide

@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;

public int logic() {
 	PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
 	prototypeBean.addCount();
 	return prototypeBean.getCount();
}
  • 스프링에서 제공하는 기능
  • ObjectFactory: 기능이 단순, 별도의 라이브러리 필요 없음, 스프링에 의존
  • ObjectProvider: ObjectFactory 상속, 옵션, 스트림 처리등 편의 기능이 많고, 별도의 라이브러리 필요 없음, 스프링에 의존
  • ObjectProvider 의 getObject() 를 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다. (DL)

2. JSR-330 Provider

  • 마지막 방법은 javax.inject.Provider 라는 JSR-330 자바 표준을 사용하는 방법이다.
  • 이 방법을 사용하려면 javax.inject:javax.inject:1 라이브러리를 gradle에 추가해야 한다.
//implementation 'javax.inject:javax.inject:1' gradle 추가 필수
@Autowired
private Provider<PrototypeBean> provider;

public int logic() {
 	PrototypeBean prototypeBean = provider.get();
 	prototypeBean.addCount();
 	return prototypeBean.getCount();
}
  • get() 메서드 하나로 기능이 매우 단순하다.
  • 별도의 라이브러리가 필요하다.
  • 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용할 수 있다

그러면 프로토타입 빈을 언제 사용할까? 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다. 그런데 실무에서 웹 애플리케이션을 개발해보면, 싱글톤 빈으로 대부분의 문제를 해결할 수 있기 때문에 프로토타입 빈을 직접적으로 사용하는 일은 매우 드물다.
ObjectProvider , JSR330 Provider 등은 프로토타입 뿐만 아니라 DL이 필요한 경우는 언제든지 사용할 수 있다.

실무에서 자바 표준인 JSR-330 Provider를 사용할 것인지, 아니면 스프링이 제공하는 ObjectProvider를 사용할 것인지 고민이 될 것이다. ObjectProvider는 DL을 위한 편의 기능을 많이 제공해주고 스프링 외에 별도의 의존관계 추가가 필요 없기 때문에 편리하다. 만약(정말 그럴일은 거의 없겠지만) 코드를 스프링이 아닌 다른 컨테이너에서도 사용할 수 있어야 한다면 JSR-330 Provider를 사용해야한다.

스프링을 사용하다 보면 이 기능 뿐만 아니라 다른 기능들도 자바 표준과 스프링이 제공하는 기능이 겹칠때가 많이 있다. 대부분 스프링이 더 다양하고 편리한 기능을 제공해주기 때문에, 특별히 다른 컨테이너를 사용할 일이 없다면, 스프링이 제공하는 기능을 사용하면 된다.

0개의 댓글