Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

UpDown Dev Story

빈 스코프란 본문

Spring

빈 스코프란

updown 2021. 6. 19. 06:40

1. Bean 스코프

빈 스코프는 무엇인가?

말그대로 빈이 사용되어지는 범위(?)인데, 빈이 앱이 구동되는 동안 한개만 만들어서 쓸 것인지 HTTP요청마다 생성해서 쓸 것인지 등등를 결정하는 것이 스코프임.
보통의 스프링 빈은 스프링 앱이 구동 될때 한번에 ApplicationContext에서 한 번에 모두 생성해서 하나의 클래스는 한 개의 빈만 가지지만 (Singleton), 때에 따라서는 HTTP요청마다 (Request) 다른 빈을 생성해서 쓸건지, 매번 사용될 때 마다 (Prototype) 빈을 생성해서 쓸건지 설정해서 쓸 수도 있다.

빈 스코프의 종류는??


링크
spring docs에서 가져온 건데 불친절하게 해석하자면,,,,

ScopeDescription

singleton (기본값) 스프링 IoC 컨테이너당 하나의 인스턴스만 사용 - 한마디로 앱이 구동되는 동안 하나만 쓴다는 거임
prototype 매번 새로운 빈을 정의해서 사용
request HTTP라이프 사이클 마다 한개의 빈을 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context
session HTTP 세션마다 하나의 빈을 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context
application ServeltContext라이프사이클 동안 한개의 빈만 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context
websocket websocket 라이프사이클 안에서 한개의 빈만 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context

2. ProtoType 스코프를 사용하는 방법

그렇다면 스프링 빈 스코프를 어떻게 정의하고 사용할 수 있을까?

@Component
@Scope(value = "prototype")
public class ProtoType {
}

바로 컴포넌트로 등록 하면서, @Scope를 붙여주면 된다. value의 값으로는 위에서 언급한 값이 Scope이름이 String으로 들어가게 되는데...

  • @Scope(value = "prototype")
  • @Scope(value = "singleton")
  • @Scope(value = "request")
  • @Scope(value = "session")
  • @Scope(value = "application")
  • @Scope(value = "websocket")
    이렇게 쓰면 된다

하지만 , 문제가 발생하는 경우가 있음...

바로 Sinlgeton 스코프의 빈이 prototype의 빈을 주입받는 경우임
말로하면 이해가 안가니까 직관적으로 코드로 보자면

@Component
public class Single {

    @Autowired
    ProtoType protoType;


    public ProtoType getProtoType() {

    }
}

코드에서 보는 것 과 같이 싱글톤 스코프의 빈이 프로토타입 빈을 주입받으면 싱글톤의 프로토타입 빈은 매번 바뀌지 않고 같은 빈이 쓰임

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    Single single;

    @Autowired
    ProtoType protoType;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("---singleton---");
        System.out.println(context.getBean(protoType.getClass()));
        System.out.println(context.getBean(protoType.getClass()));
        System.out.println(context.getBean(protoType.getClass()));

        System.out.println("---prototype---");
        System.out.println(single);
        System.out.println(single);
        System.out.println(single);

        System.out.println("---prototype by singleton---");
        System.out.println(single.getProtoType());
        System.out.println(single.getProtoType());
        System.out.println(single.getProtoType());
    }
}

결과는...


이렇게 프로토타입이지만 매번 같은 빈을 사용함

이유는???

singleton 빈 ApplicationContext가 처음 앱을 구동할때 빈을 만들고 빈을 주입해서 앱이 종료될 때 까지 계속 사용 되기 때문에 singleton 빈 안에 있는 prototype 빈도 처음 주입된 채로 그대로 사용된다.

그렇다면 해결 방법은?!

두가지 방법이 있는데,,,
1. proxy mode를 이용하는 방법

@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ProtoType {
}

프로토타입으로 쓸 빈에 @Scope 어노태이션에 proxyMode = ScopedProxyMode.TARGET_CLASS를 넣어주면 된다. 만약에 해당 오브잭트가 클래스가 아니라 인터페이스라면 proxyMode = ScopedProxyMode.INTERFACES도 쓸 수가 있다.

2. ObjectProvider객체를 사용하는 방법

@Component
public class Single {

    @Autowired
    ObjectProvider<ProtoType> protoType;

    public ProtoType getProtoType() {
        return protoType.getIfAvailable();
    }
}

ObjectProvider< >를 용해서 매번 빈을 주입하는 방법 도 있다. 하지만,
1번이 POJO를 유지하기 때문에 기선이형은 프록시모드를 이용한 방법을 추천해 줬음!

3. Outro

proxy mode가 어떻게 작동하는지 간단하게 설명하자면,


그림에서 처럼 ApplicationConxtext가 빈을 처음에 생성할 때 proto 빈을 주입받는게 아니라 proto 클래스를 상속받은(타입이 같은) proxy클래스를 만들어서 빈으로 등록하고 proxy클래스에서 내부적으로 매번 새로운 proto빈을 사용하게 끔 설계 되어있다.

 

Comments