Dependency Lookup; 의존관계 조회/탐색
- 의존관계를 외부에서 주입받는 게 아니라 직접 필요한 의존관계를 찾는 것
import org.springframework.beans.factory.annotation.Lookup;
@Scope("singleton")
static class ClientBean {
@Lookup
public PrototypeBean getPrototypeBean() {
return null; // 실제 구현에서는 무시
}
public int logic() {
PrototypeBean prototypeBean = getPrototypeBean();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
⇒ @Lookup 애너테이션을 통해 매번 새로운 PrototypeBean 인스턴스를 주입받을 수 있다.
* 참고 상황
Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
싱글톤 빈 A가 아마도 A에 대한 각 메서드 호출에 싱글톤이 아닌 (프로토타입) 빈 B를 사용해야 한다고 가정하자. 컨테이너는 싱글톤 빈 A를 단 한 번만 생성하므로 속성을 설정할 수 있는 기회를 단 한 번만 얻는다. 컨테이너는 필요할 때마다 빈 A에게 빈 B의 새로운 인스턴스를 제공할 수 없다. [-네이버 파파고]
싱글톤 빈은 생성 시점에서만 의존관계를 주입받기 때문에 프로토타입 빈이 새로 생겨도 싱글톤 빈과 함께 유지된다.
@Lookup
- Spring 프레임워크에서 사용되는 애너테이션
- 주로 @Scope("singleton")으로 설정된 빈이 @Scope("prototype")으로 설정된 빈을 사용해야 할 때 활용
- 즉, 싱글톤 빈 안에서 프로토타입 빈을 주입받을 때 사용
- 스프링 컨테이너에 있는 싱글톤 빈을 주입하는 대신, 메서드 호출 시점마다 해당 빈을 새로 생성하여 주입
- 애너테이션 설명 및 사용 방법
위에도 많은 설명이 있지만 간단하게 이 부분을 먼저 참고하면, 필드나 생성자에 붙이는 게 아닌 메서드에 붙여 사용하는 것을 알 수 있다. 그리고 초록색 주석을 보면, 이 애너테이션의 역할은 타겟 빈(의 이름)을 조회해 주는 것이라고 한다. (= getBean(class), getBean(String); 프로토타입 빈은 이 과정에서 매번 새로운 빈이 생성된다) 앞에서부터 계속 프로토타입 빈을 언급했지만 사실 이 애너테이션 자체는 특정 스코프에 제한되지는 않는다. 싱글톤 빈도 조회된다는 사실! 그리고 디폴트가 ""로 따로 명시되지 않은 경우에는 타겟 빈을 찾을 때 애너테이션이 붙은 메서드의 반환 타입을 기반으로 찾는다.
The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.
위에 따르면, CGLIB의 바이트코드 조작 라이브러리를 사용해 메서드를 오버라이드하는 서브클래스를 만든다고 한다. CGLIB은 proxy 객체를 생성해 주는데, 이 proxy 객체가 @Lookup이 붙은 메서드를 호출할 때마다 Spring 컨테이너에서 해당 메서드에 대응하는 빈을 찾아서 반환하는 역할을 한다. (proxy 객체에서 해당 메서드 호출을 가로채고 Spring이 자동으로 오버라이드한 메서드를 실행한다 - chatGPT) 그렇기 때문에 return null;이 의미없는 것이다.
tip.
Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. See Scoped Beans as Dependencies.
인프런 김영한 님의 강의 - 스프링 핵심원리 기본편에서는 사실 이 방법보다 ObjectFactory/ObjectProvider(springframework), Provider(jakarta)의 방법을 더 추천하셨다! 써 놓고보니 @Lookup보다 이들을 다뤘다면 더 좋았을 것 같다는 생각이 들지만... 이 기회에 자세히 알아봐서 유익했다.
이들은 모두 스프링 컨테이너에서 대신 찾아주는 기능만 제공한다. 내가 직접 조회해 보는 것이 아니라 provider를 통해 대신 조회해 본다고 생각하면 된다. 조금 전의 @Lookup처럼 특정 스코프에 제한되지 않고 DL이 필요한 경우에 자유롭게 사용할 수 있다.
1. springframework의 ObjectProvider
- 스프링에 의존
- ObjectFactory를 상속하는 인터페이스
- ObjectFactory보다 더 많은 편의기능들을 제공
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import static org.assertj.core.api.Assertions.assertThat;
public class SingletonWithPrototypeTest1 {
@Test
void prototypeFind() {
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);
}
@Scope("singleton")
static class ClientBean {
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
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");
}
}
}
2. springframework의 ObjectFactory
- 스프링에 의존
- getObject() 메서드만 있음
- ObjectProvider보다 더 상위의 인터페이스
import org.springframework.beans.factory.ObjectFactory;
@Scope("singleton") // 생략 가능
static class ClientBean {
@Autowired
private ObjectFactory<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
3. javax/jakarta의 Provider
- 별도의 라이브러리 필요
- 자바 표준이므로 스프링이 아닌 다른 컨테이너에도 사용 가능
- 기능이 단순 - get() 메서드만 있음
import jakarta.inject.Provider;
@Scope("singleton") // 생략 가능
static class ClientBean {
@Autowired
private Provider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
}
Lookup (Spring Framework 6.1.12 API)
An annotation that indicates 'lookup' methods, to be overridden by the container to redirect them back to the BeanFactory for a getBean call. This is essentially an annotation-based version of the XML lookup-method attribute, resulting in the same runtime
docs.spring.io
Method Injection :: Spring Framework
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the depende
docs.spring.io
'Spring' 카테고리의 다른 글
[Spring] 컴파일 과정, 의존성 추가와 플러그인 설치 차이 (0) | 2024.09.30 |
---|---|
[Spring] Filter, Interceptor, AOP 비교 (0) | 2024.09.08 |
[Spring] 객체 지향 프로그래밍의 특징과 좋은 객체 지향 설계의 원칙 5가지, SOLID (0) | 2024.08.17 |
[Spring] Intellij 단축키 모음 (-ing) (0) | 2024.06.26 |
[Spring] MyBatis Repository 인터페이스와 XML 매핑 파일 연결 (2) | 2024.06.12 |