싱글톤 빈과 프로토타입 빈을 함께 사용시, 프로토타입 빈을 사용할 때마다 항상 새로운 빈을 생성하도록 하는 방법 3가지를 알아본다.
1. 스프링 컨테이너에 요청
싱글톤 빈이 프로토타입 빈을 사용할 때마다 스프링 컨테이너에 새로 요청
- src/test/java/hello/core/scope/PrototypeProviderTest.java
package hello.core.scope;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import static org.assertj.core.api.Assertions.assertThat;
public class PrototypeProviderTest {
@Test
void providerTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ClientBean.class, PrototypeBean.class);
ClientBean clientBean1 = ac.getBean(ClientBean.class);
int count1 = clientBean1.logic();
assertThat(count1).isEqualTo(1);
ClientBean clientBean2 = ac.getBean(ClientBean.class);
int count2 = clientBean2.logic();
assertThat(count2).isEqualTo(1);
}
static class ClientBean {
@Autowired
private ApplicationContext ac;
public int logic() {
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init " + this);
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
}
핵심 코드)
@Autowired
private ApplicationContext ac;
public int logic() {
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
· ApplicationContext 의 getBean() 메서드를 통해 항상 새로운 프로토타입 빈 생성
의존관계를 외부에서 주입(DI) 받는게 아닌, 직접 필요한 의존관계를 찾는 것을 의존관계 조회(탐색)(Dependency Lookup, 줄여서 DL) 이라고 함
· 스프링 ApplicationContext 를 주입받으면, 스프링 컨테이너에 종속적인 코드가 되고, 단위테스트도 어려워짐
지정한 프로토타입 빈을 컨테이너에서 찾아주는(DL) 무언가가 필요함
# 의존관계 조회(탐색)(Dependency Lookup, DL) : 직접 필요한 의존관계를 찾음
2. ObjectFactory, ObjectProvider
# ObjectFactory : 기능이 단순, 별도 라이브러리 필요 없음, 스프링 의존
# ObjectProvider : 지정한 빈을 스프링 컨테이너에서 찾음(DL) - getObject() 메서드 호출
ObjectFactory 에 편의 기능(Optional, stream 처리 등)이 추가되어 만들어짐(ObjectFactory 상속)
별도 라이브러리 필요 없음, 스프링 의존
- src/test/java/hello/core/scope/PrototypeProviderTest2.java
...
static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
...
· ObjectProvider<T> 의 getObject() 메서드를 통해 항상 새로운 프로토타입 빈이 생성됨
getObject() 메서드 호출시, 내부에서 스프링 컨테이너를 통해 해당 빈을 찾아 반환(DL)
· 스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위테스트, mock 코드를 만들기 쉬움
ObjectProvider<T> 가 DL 기능만 제공하기 때문
3. JSR-330 Provider
javax.inject.Provider 이라는 JSR-330 자바 표준 사용
# JSR-330 Provider : 지정한 빈을 컨테이너에서 찾음(DL)
get() 메서드 하나로 기능이 매우 단순함
별도 라이브러리 필요, 자바 표준이므로 스프링이 아닌 다른 컨테이너에서 사용 가능
* JSR-330 표준 어노테이션(의존성 주입)
스프링 부트 3.0 부터 jakarta.inject.Provider 사용
JSR 330 표준 어노테이션 사용하기 (문서번역모음) : https://docs.oofbird.me/spring/core/ioc/1_11.html
JSR 330 표준 어노테이션 사용하기
JSR 330 표준 어노테이션 사용하기
docs.oofbird.me
JSR 330 표준 어노테이션 사용하기 (표준프레임워크 포털) : https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte4.1:fdl:ioc_container:jsr_330_standard_annotations
gradle 에 라이브러리 추가)
- build.gradle (스프링부트 3.0 미만)
...
implementation 'javax.inject:javax.inject:1'
...
- build.gradle (스프링부트 3.0 이상)
...
implementation 'jakarta.inject:jakarta.inject-api:2.0.1'
...
javax.inject.Provider 참고용 코드 - 스프링부트 3.0 미만)
package javax.inject;
public interface Provider<T> {
T get();
}
- src/test/java/hello/core/scope/PrototypeProviderTest3.java
...
static class ClientBean {
@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
...
· Provider 의 get() 메서드를 통해 항상 새로운 프로토타입 빈이 생성됨
get() 메서드 호출시, 내부에서 스프링 컨테이너를 통해 해당 빈을 찾아 반환(DL)
· 자바 표준이면서, 기능이 단순하므로 단위테스트, mock 코드를 만들기 쉬움
Provider 가 DL 기능만 제공하기 때문
4. 정리
실무에서 웹 애플리케이션을 개발하다 보면, 대부분 싱글톤 빈으로 문제를 해결할 수 있기 때문에 프로토타입 빈을 직접적으로 사용하는 일은 매우 드물다.
프로토타입 빈은 매번 사용할 때마다 의존관계 주입이 완료된 새로운 객체가 필요할 때 사용한다.
ObjectProvider, JSR-330 Provider 등은 프로토타입 빈 뿐만 아닌, DL이 필요할 때 언제든지 사용할 수 있다.
* 스프링이 제공하는 메서드에 @Lookup 어노테이션을 사용하는 방법도 있지만, 고려할 부분이 많다.
@Lookup : https://xephysis.tistory.com/25
@Lookup
들어가는 말 spring core reference 문서 뒤적거리다가 처음 보는 어노테이션(개념)이 있어서 궁금해서 정리 간단하게는 @Lookup 어노테이션이 달린 메서드가 호출될 때 스프링이 bean을 찾아서 리턴해준
xephysis.tistory.com
* 실무에서 어느것을 사용할 것인가?
ObjectProvider 는 DL을 위한 편의 기능을 많이 제공하고 스프링 외에 별도 의존관계 추가가 필요 없기 때문에 편리하다.
JSR-330 Provider 는 스프링이 아닌 다른 컨테이너에서 사용할 때 사용한다.
스프링을 사용하다 보면 다른 기능들도 자바 표준과 스프링 제공 기능이 겹칠 때가 많다.
대부분 스프링이 더 다양하고 편리한 기능을 제공해주기 때문에, 특별히 다른 컨테이너를 사용할 일이 없다면 스프링 제공 기능을 사용한다.
스프링 환경 : ObjectProvider 사용
다른 컨테이너 환경 : JSR-330 Provider 사용
참고링크 : 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?cid=325969
스프링 핵심 원리 - 기본편| 김영한 - 인프런 강의
현재 평점 5.0점 수강생 49,600명인 강의를 만나보세요. 스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다. 스프링 기본 기능, 스프
www.inflearn.com
'강의 실습 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
| request 스코프 예제 만들기 (0) | 2026.05.10 |
|---|---|
| 웹 스코프 (0) | 2026.05.09 |
| 프로토타입 스코프 - 싱글톤 빈과 함께 사용시 문제점 (0) | 2026.05.07 |
| 프로토타입 스코프 (0) | 2026.05.06 |
| 빈 스코프란? (0) | 2026.05.05 |
댓글