Study/Spring
Spring. ObjectProvider
염몽이
2024. 9. 8. 16:27
ObjectProvider
ObjectProvider
를 공부하다가 의문이 생겨 정리할 겸 글을 작성한다.
아래의 코드는 prototype
Bean 옵션을 사용하고 있다.
PrototypeBean
클래스를 사용하여 주입받는ClientBean
이 있다.
package hello.core.scope;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.inject.Provider;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.assertj.core.api.Assertions.*;
public class SingletonWithPrototypeTest1 {
@Test
void prototypeFind() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
prototypeBean1.addCount();
assertThat(prototypeBean1.getCount()).isEqualTo(1);
PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
prototypeBean2.addCount();
assertThat(prototypeBean1.getCount()).isEqualTo(1);
}
@Test
void singletonClientUsePrototype() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class, ClientBean.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);
}
@Scope("singleton")
static class ClientBean {
//private final PrototypeBean prototypeBean; //생성시점에 주입
private ObjectProvider<PrototypeBean> prototypeBeanProvider; // spring 기능: 권장
//private Provider<PrototypeBean> prototypeBeanProvider; // JSR-330 Provider: 스프링이 아닌 다른 컨테이너에서도 사용 가능
public int logic() {
//prototypeBean.addCount();
//return prototypeBean.getCount();
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@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");
}
}
}
ObjectProvider
생성 사이클:
- 1. 생성자 주입: `ClientBean`에 `ObjectProvider<PrototypeBean>`가 주입된다.
이때 `ObjectProvider`는 단순히 프로토타입 빈을 제공할 수 있는 객체로 주입될 뿐,
실제로 `PrototypeBean`의 인스턴스를 미리 생성해서 가지고 있지는 않는다.
- 2. `getObject()` 호출: `ObjectProvider.getObject()`가 호출될 때마다
스프링 컨테이너에서 새로운 프로토타입 빈을 요청하고 반환한다.
따라서, 매번 `getObject()`를 호출할 때마다 새로운 `PrototypeBean` 인스턴스가 생성된다.
ObjectProvider
의 주요 역할:
1. 지연된 빈 생성: 빈을 즉시 주입받는 것이 아니라, 필요할 때 getObject()를 호출하여 빈을 동적으로 가져온다.
2. 프로토타입 빈 사용: 특히 프로토타입 빈의 경우, 매번 getObject()를 호출할 때마다 새로운 인스턴스를 반환한다.
3. 유연성: 주입 시점에 빈을 결정하지 않고, 런타임에 필요할 때 빈을 가져오는 방식으로 더 유연하게 사용할 수 있다.
정리:ObjectProvider
는 조회 기능만 제공하는 클래스이다.
스프링 컨테이너에 등록된 빈을 동적으로 조회하며, 빈을 직접 생성하지 않는다.
프로토타입 빈은 조회할 때마다 새로 생성된다. ObjectProvider.getObject()
를 호출할 때,
스프링 컨테이너가 해당 빈을 생성하여 반환하는 방식이다.
따라서 ObjectProvider는 빈을 조회하는 역할을 담당하고, 빈의 생성은 스프링 컨테이너가 관리한다.
실무에서는 Dipendeny Lookup (DL) 을 위한 편의 기능을 많이 제공해주고
스프링 외에 별도의 의존관계 추가가 필요 없기 때문에ObjectProvider
를 사용하면 된다.
만약 코드를 스프링이 아닌 다른 컨테이너에서도 사용할 수 있어야 한다면 JSR-330 Provider를 사용해야한다.
스프링이 제공하는 기능을 사용하면 된다.