본문 바로가기
Programming/Java

[Spring Framework] Bean의 Scope

by minux_s 2020. 12. 22.
728x90

지금까지 등록한 Bean들은 사실상 Single Tone Scope의 bean들만 사용해왔다.

*아무런 설정 하지 않으면 기본 scope이 single tone이다

@Conponent
public class Single{
}

 

Single Tone이란?

이 application 전반에 걸쳐서 해당 bean에 인스턴스가 오직 한 개 뿐이다! 라는 뜻이다.

 

Proto Type  Scope이란?

매번 새로운 객체를(인스턴스를) 만들어서 써야하는 scope이다

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

이러면 이 bean을 받아올 때마다 새로운 인스턴스가 된다.

bean을 받아 올 때만!!

 

@Component
public class AppRunner implements Appliation Runner {
     @Autowired
     ApplicationContext ctx;
     
     @Override
     public void run(ApplicationArguments args) throws Exception {
          System.out.println("proto");
          
          System.out.println(ctx.getBean(Proto.class));
          System.out.println(ctx.getBean(Proto.class));
          System.out.println(ctx.getBean(Proto.class));
          
          System.out.println("single");
          
          System.out.println(ctx.getBean(Single.class));
          System.out.println(ctx.getBean(Single.class));System.out.println(ctx.getBean(Single.class));
     }
}
     

proto type과 single을 호출해서 bean의 값을 살펴보면

proto는 매번 다른 값, single은 항상 동일한 값의 bean이 나오는 것을 볼 수 있다.

 

이제, proto와 single이 섞여서 사용되면 얘기가 복잡해진다.

 

1) Proto type bean에서 single을 사용할 때

@Component @Scope("prototype")
public class Proto {
     @Autowired
     Single single;
}

이 때는 문제가 되지 않는다.

왜냐하면 어차피 single이라는 인스턴스는 single tone scope이기 때문에 매 번 같은 인스턴스가 의도한 대로 들어오고, proto type은 proto type scope이기 때문에 매 번 꺼낼 때마다 새로운 인스턴스가 나가는데, 대신에 이 proto type의 bean은 새롭지만 proto type의 bean이 참조하는 single tone scope의 bean은 항상 동일.

의도한 대로 흘러갈 것이기 때문에 문제 없음!

 

2) Single Tone bean이 Proto type bean을 사용할 때

@Component
public class Single{
     @Autowired
     private Proto proto;
     
     public Proto getProto(){
          return proto;
     }
}

Single tone scope의 bean은 single tone scope이기 때문에 인스턴스가 한 번만 만들어진다.

한 번 만들어질 때 이 proto type scope의 프로퍼티도 이미 세팅이 되어있다.

그렇기 때문에 이 single tone scope의 bean읠 계속해서 쓸 때, proto type scope의 프로퍼티가 변경되지 않는다.

이게 문제다.

@Component
public class AppRunner implements Appliation Runner {
     @Autowired
     ApplicationContext ctx;
     
     @Override
     public void run(ApplicationArguments args) throws Exception {
          System.out.println("proto");
          
          System.out.println(ctx.getBean(Proto.class));
          System.out.println(ctx.getBean(Proto.class));
          System.out.println(ctx.getBean(Proto.class));
          
          System.out.println("single");
          
          System.out.println(ctx.getBean(Single.class));
          System.out.println(ctx.getBean(Single.class));
          System.out.println(ctx.getBean(Single.class));
          
          System.out.println("proto by single");
          
          System.out.println(ctx.getBean(Single.class).getProto());
          System.out.println(ctx.getBean(Single.class).getProto());
          System.out.println(ctx.getBean(Single.class).getProto());
     }
}
     

실행 시 

다음과 같이 proto type scope가 같은 값이 나온다.

 

이걸 해결하는 방법 중 가장 쉬운 방법은 proxyMode를 설정해주면 된다.

proxyMode의 기본값은 default로 되어있고, default는 proxy를 사용하지 않는다는 의미이다.

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

proxyMode란 얘를 proxy로 감싼다는 의미이다.

다른 bean들이 이 bean을 사용 할 떄, proxy로 감싼 bean을 사용하게 해라! 라고 설정한 것이다.

 

왜 proxy로 감싸야 하는가?

다른 인스턴스 들이 이 proto type scope bean을 직접 참조하면 안되고 proxy를 거쳐서 참조해야한다.

직접 쓰면 바꿔줄 여지가 없다. 이 proto type을 매번 새로운 것으로 바꿔줘야하는데 바꿔줄 수가 없기 때문에 바꿀 수 있는 proxy로 감싸도록 이 class를 상속받은 class로 만들어서 proxy를 만들어 주는 CG library라는 Third Party의 라이브러리가 있다.

 

이 라이브러리는 class도 proxy로 만들수 있게 해준다.

원래 JAVA JDK 안에 있는 Dynamic Proxy는 inteface의 proxy 밖에 못 만든다. 그렇기 때문에 proxyMode = ScopedProxyMode.TARGET_CLASS라고 모드를 알려줬다.

class 기반의 proxy를 만들어서 CG library 기반의 class를 상속받은 proxy를 만들도록 알려줌.

 

민약 interface가 있었다면 여기서 proxyMode = ScopedProxyMode.INTERFACES를 쓸 수 도 있다. 

이러면 JDK의 interface기반의 proxy를 쓴다.

 

실제 인스턴스를  proxy 인스턴스가 만들어지고, 이 proxy 인스턴스가 bean으로 등록이 된다.

 

Proxy 이외의 방법으로는 

@Component
public class Single{
	@Autowired
    pricate ObjectProvider<Proto> proto;
    
    public Proto getProto(){
    	return proto.getIfAvailable();
    }
}

코드를 수정하는 방법이 있는데, 이 방법은 소스코드 자체에 spring 코드가 들어가기 떄문에 조금 그렇다고 한다...

차라리 bean을 선언하는 부분에다가만 spring 관련관 scopedproxy를 쓰는게 낮지 않을까 한다.

 

짧은 scope를 가진 bean들을 주입받을 때는 반드시 위에 설명한 부분을 참고해야한다!!

 

 

추가

※ Single tone 객체를 사용할 때 주의해야 할 점은 property가 공유가 될 수 있다!!

@Component
public class Single{

	@Autowired
    private Proto proto;
    
    int value = 0;
    
    public Proto getProto(){
    	return proto;
    }
}

이 경우의 값이 thread safe할 것이라고 보장 받을 수 없다.

multi thread 환경에서 이 값이 여러군데에서 바뀌는데 A thread와 B thread가 바꾼 값이 서로 동일한 곳을 보고 있을 수 있다.

고로 Thread Safe한 방법으로 코딩해야한다!

 

 

728x90

'Programming > Java' 카테고리의 다른 글

[MQTT] MQTT broker 종류  (0) 2021.01.29
[Javascript Framework] Property  (0) 2020.12.28
[Spring Framework] IOC Container  (0) 2020.12.06
[Spring Framework] AutoWired  (0) 2020.12.06
[Spring Framework] 스프링 소개  (0) 2020.12.02

댓글