springframework

o.s.w IoC 핵심기술. @Bean 의 @Scope

Jungsoomin :) 2020. 8. 20. 00:13

@Bean 에노테이션이나 @Component 에노테이션 과 같이 @Scope 에노테이션을 붙여 빈의 스코프를 결정 할 수 있다는 사실을 기억한다. 

 

빈의 스코프는 기본이 Sington 이며 같은 레퍼런스를 갖는다, 

하지만 @Scope 에노테이션의 속성값으로 webSocket, prototype, Session 등을 지정할 수 있다.

 

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

}

@Component
@Scope("singleton")
public class Single {

}

요렇게!

 

 

프로토 타입 빈은 매번 다른 인스턴스를 생성하며, 싱글톤 빈이 DEFAULT 값으로 언제나 같은 객체를 리턴한다.

 

prototype은 Thread-Safe하지 않다. 여기까지는 이야기가 간단하며, 프로토 타입 빈은 또한 스프링에서 생명주기를 전부 관리해주지 않게 된다.


문제는 두 가지 스코프를 같이 엮어 사용할때 발생한다는 점이다.

 

  1. 프로토 타입 스콮 빈에서 싱글톤타입 빈을 사용할 경우, 언제나 같은 객체를 이용하게 되므로 문제가 생기지않는다.

문제가 생기는 지점은 , , 글톤 스콮 빈이 프로토 타입 스콮 빈을 사용할 경우이다..

  1. 스프링 컨테이너 생명주기와 빈의 생명주기에 관계에 따라 스프링 컨테이너 구동 작업중에 빈의 생성과 의존성이 주입되는데, 여기서 싱글톤 타입의 빈은 값이 완성되므로, 안에 포함하는 값또한, 완성되어 고정된다는 점 에서 발생하는 문제이다.

즉 언제나 같은 객체의 레퍼런스가 튀어나와버린다.

package me.soomin.demospring51;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    ApplicationContext ctx;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        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());
        
        //싱글톤 스콮 빈이 내포하는 프로토 타입 스콮 빈은...매번 값이 ...같다.
    }
}

 


이문제를 해결하는 방법은 @Scope 에노테이션의 속성proxyMode << 속성을 사용하는 방법이다.

 

@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
// 클래스를 기반으로한 프록시로 감싸주어라 라는 속성
public class Proto {


}

 ScopedProxyMode의 상수 값을 이용하여 프록시 사용여부를 지정하며 기본값은 ScopedProxyMode.DEFAULT이다
즉, 프록시를 사용하지않겠다는 의미이다

 

1. 빈이 인터페이스라면 ScopedProxyMode.INTERFACE , 클래스라면 ScopedProxyMode.TARGET_CLASS 를 사용하여 프록시 객체를 만들어 빈객체를 감싼다.

 

@Scope 에노테이션의 proxyMode 속성의 상수 ScopedProxyMode.TARGET_CLASS 는 클래스 기반의 프록시로 해당 빈을 감싸주어라라는 의미이다. 즉 상속해서 만들어낸 프록시 객체를 거쳐서 prototype 빈에 접근 하라는 의미이다

 

* 이렇게 할 시에, 만들어지는 빈은 프록시 객체의 빈 이며, 주입되는 빈 또한 프록시 객체의 빈 이 된다.


핵심 내용 : 싱글톤 객체를 사용할때 prototype 빈이나 매번 생성되는 값이나 객체를 사용할 경우에 Thread - Safe 하지 않으므로, 멀티스레드 환경에서 값이 뒤죽박죽변할 수 있다.

싱글톤 객체의 인스턴스 멤버는 서로 같은 값을 참조 하게 되므로,  뒤죽박죽 엮여버린다.