스프링 - 빈 스코프

meluu_·2023년 8월 17일
0

스프링

목록 보기
8/27
post-thumbnail

🌿 빈 스코프


의미 : 빈이 존재할 수 있는 범위

✔️ 스프링이 지원하는 스코프

  • 싱글톤 : 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프

  • 프로토타입 : 스프링 컨테이너는 프로토 타입 빈의 생성과 의존관계 주입까지만 관여(더는 관리 x)

  • 웹관련 스코프

    • request : 웹 요청이 들어오고 나갈때 까지 유지되는 스코프
    • session : 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프
    • application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

✔️ 컴포넌트 스캔 등록

자동

@Scope("prototype")
@Component
public class HelloBean {}

수동

// AppConfig 같은 곳
@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
 return new HelloBean();
}

☘️ 프로토타입 스코프


싱글톤 스코프 빈 조회 : 항상 같은 인스턴스의 스프링 빈 반환
프로토타입 스코프 빈 조회 : 항상 새로운 인스턴스를 생성해서 반환

프로토타입 빈 요청시
1. 새로운 빈 생성 + 의존관계 주입을 한다.
2. 생성 빈 클라이언트에 반환
3. 같은 요청이 오면 새로운 프로토타입 빈 생성 및 반환

핵심은 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만
처리한다는 것

특징

  • 종료 메서드가 호출 X
  • 그래서 프로토타입 빈은 프로토타입 빈을 조회한 클라이언트가 관리해야 한다. 종료 메서드에 대한 호출도 클라이언트가 직접 해야한다.


✔️ 싱글톤 빈과 함께 사용시 문제점


싱글톤 빈 안에 프로토 타입 빈을 넣었다고 하자

@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 기능만 필요하다.


🧫ObjectFactory, ObjectProvider

지정한 빈을 컨테이너에서 대신 찾아주는 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 상속, 옵션, 스트림 처리등 편의 기능이 많고, 별도의 라이브러리 필요 없음, 스프링에 의존



🌱 JSR-330 Provider

자바 표준 사용


스프링 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;
    }
  • provider 의 get() 을 호출시 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환 (DL)
  • 자바 표준, 단위 테스트 mock 코드 수월
  • Provider 는 지금 딱 필요한 DL 정도의 기능만 제공

✔️ 특징

  • 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을 사용하면 된다.

Providerrequest 스코프 빈의 생성을 지연 할 수 있다.
즉 요청이 올때까지 빈의 생성을 지연한다는 것이다.


✔️ 프록시

// 적용 대상이 인터페이스면 INTERFACES 를 선택
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
  • CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.

  • 가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.

  • 꼭 웹 스코프가 아니어도 프록시는 사용할 수 있다.


컨트롤러와 mvc를 배우고 나서 필요할때 다시 학습하면 좋을 거라 생각한다.


🔖 학습내용 출처

스프링 핵심 원리 - 기본편

profile
열심히 살자

1개의 댓글

comment-user-thumbnail
2023년 8월 17일

좋은 글 감사합니다. 자주 방문할게요 :)

답글 달기