의미 : 빈이 존재할 수 있는 범위
싱글톤 : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
프로토타입 : 스프링 컨테이너는 프로토 타입 빈의 생성과 의존관계 주입까지만 관여(더는 관리 x)
웹관련 스코프
자동
@Scope("prototype")
@Component
public class HelloBean {}
수동
// AppConfig 같은 곳
@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
return new HelloBean();
}
싱글톤 스코프 빈 조회 : 항상 같은 인스턴스의 스프링 빈 반환
프로토타입 스코프 빈 조회 : 항상 새로운 인스턴스를 생성해서 반환
프로토타입 빈 요청시
1. 새로운 빈 생성 + 의존관계 주입을 한다.
2. 생성 빈 클라이언트에 반환
3. 같은 요청이 오면 새로운 프로토타입 빈 생성 및 반환
핵심은 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만
처리한다는 것
특징
싱글톤 빈 안에 프로토 타입 빈을 넣었다고 하자
@Component
public class ClientBean {
private final PrototypeBean prototypeBean;
@Autowired
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int getPrototypeBean() {
return prototypeBean.getCount();
}
public void addCount() {
prototypeBean.addCount();
}
}
}
@Scope("prototype")
@Component
public class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
}
@Test
@DisplayName("스코프 테스트")
void run() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
ClientBean cb1 = ac.getBean(ClientBean.class);
cb1.addCount();
ClientBean cb2 = ac.getBean(ClientBean.class);
cb2.addCount();
int tmp = cb1.getPrototypeBean();
System.out.println("tmp = " + tmp);
Assertions.assertThat(cb2.getPrototypeBean()).isEqualTo(2);
}
//결과 성공적으로 돌아간다. 즉 카운트는 2가 되었다는 것
프로토타입의 생성 및 의존관계 주입이 끝난 시점에서 스프링 컨테이너는 관리에서 손을 땐다. 그렇기에 싱글톤 빈 안에 있는 프로토타입 빈은 새롭게 생성되는 것이 아니라 상태가 유지된다.
Dependency Lookup(DL) : 의존관계 조회(탐색) 의존관계를 외부에서 주입(DI) 받는게 아니라 이렇게 직접 필요한 의존관계를 찾는 것
스프링의 애플리케이션 컨텍스트 전체를 주입받게 되면, 스프링 컨테이너에 종속적인 코드가 되고,단위 테스트도 어려워짐
즉, DL 기능만 필요하다.
지정한 빈을 컨테이너에서 대신 찾아주는 DL 서비스를 제공
@Component
public class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위테스트를 만들거나 mock 코드를 만들기는 훨씬 쉬워진다.
ObjectFactory: 기능이 단순, 별도의 라이브러리 필요 없음, 스프링에 의존
ObjectProvider: ObjectFactory 상속, 옵션, 스트림 처리등 편의 기능이 많고, 별도의 라이브러리 필요 없음, 스프링에 의존
자바 표준 사용
스프링 3.0 미만 사용시
javax.inject:javax.inject:1
라이브러리를 gradle에 추가
스프링 3.0 이상 사용시
jakarta.inject:jakarta.inject-api:2.0.1
라이브러리를 gradle에 추가
@Component
public class ClientBean {
@Autowired
private Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
get()
메서드 하나로 기능이 매우 단순스프링 컨테이너를 사용하는 상황이라면 ObjectProvider
을 사용하자.
아니라면 JSR-330 사용하자.
참고 : 스프링이 제공하는
@Lookup
애노테이션 사용 방법도 있다.
request : HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프 ,
각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리
seesion : HTTP Session과 동일한 생명주기를 가짐
application : 서블릿 컨텍스트(ServletContext
)와 동일한 생명주기를 가짐
websocket : 웹 소켓과 동일한 생명주기를 가짐
request 스코프는 동시에 여러 HTTP 요청이 오면 어떤 요청이 남긴 로그인지 구분 가능
@Scope(value = "request")
를 사용해서 request 스코프로 지정
여기서 request 스코프 빈은 실제 고객의 요청이 와야 생성할 수 있다.
이를 해결하기 위해Provider
,Proxy
가 있다.
위에서 배웠던 Provider을 사용하면 된다.
Provider
은 request
스코프 빈의 생성을 지연 할 수 있다.
즉 요청이 올때까지 빈의 생성을 지연한다는 것이다.
// 적용 대상이 인터페이스면 INTERFACES 를 선택
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.
가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.
꼭 웹 스코프가 아니어도 프록시는 사용할 수 있다.
컨트롤러와 mvc를 배우고 나서 필요할때 다시 학습하면 좋을 거라 생각한다.
🔖 학습내용 출처
좋은 글 감사합니다. 자주 방문할게요 :)