의미 : 빈이 존재할 수 있는 범위
싱글톤 : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
프로토타입 : 스프링 컨테이너는 프로토 타입 빈의 생성과 의존관계 주입까지만 관여(더는 관리 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를 배우고 나서 필요할때 다시 학습하면 좋을 거라 생각한다.
🔖 학습내용 출처
좋은 글 감사합니다. 자주 방문할게요 :)