본문 바로가기

프로그래밍/Spring

빈의 스코프

빈의 스코프

스프링 빈의 범위에는 싱글톤(Singleton)과 프로토타입(Prototype)가 있다.
싱글톤 스코프는 스프링에서 별도의 설정을 하지 않은 경우 기본적으로 동작하는 방식으로 애플리케이션이 구동될 떄 하나의 인스턴스만 생성하고 여러번 호출 할때 매번 동일한 객체를 리턴한다.
반대로 프로토타입 스코프는 매번 새로운 인스턴스를 생성하여 매번 다른 객체를 리턴한다.
아래에 간단한 예시로 확인해보자.

@Component
public class Singleton {

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

이렇게 두 개의 클래스를 빈으로 등록을하는데 Protytype 클래스의 범위는 prototype으로 하였다. 이후 ApplicationContext에서 찍어보면 아래와 같은 결과를 확인할 수있다.

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    ApplicationContext context;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(context.getBean(Singleton.class));
        System.out.println(context.getBean(Singleton.class));

        System.out.println(context.getBean(Prototype.class));
        System.out.println(context.getBean(Prototype.class));
    }
}

실행결과

com.giantdwarf.scope.Singleton@27b45ea  //동일
com.giantdwarf.scope.Singleton@27b45ea
com.giantdwarf.scope.Prototype@3e17a0a1 //다름
com.giantdwarf.scope.Prototype@790a251b

서로다른 스코프의 빈 주입시 문제점

프로토타입 스코프의 빈이 싱글톤 스코프의 빈을 참조하는 경우

프로토타입 빈은 매번 꺼낼떄마다 새로운 인스턴스가 나오고 그 빈이 참조하는 싱글톤 스코프의 빈은 동일하다.(문제 없다)

@Component
@Scope("prototype")
public class Prototype {

    @Autowired
    Singleton singleton;
}

싱글톤 스코프의 빈이 프로토타입의 빈을 참조하는 경우

싱글톤 스코프의 빈은 인스턴스가 생성될 때 주입받아 세팅이 되서 프로토타입의 빈이지만 의도한대로 다른 인스턴스를 주지않고 같은 인스턴스가 나오게된다.

@Component
public class Singleton {

    @Autowired
    private Prototype prototype;

    public Prototype getPrototype() {
        return prototype;
    }
}
@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    ApplicationContext context;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(context.getBean(Singleton.class).getPrototype());
        System.out.println(context.getBean(Singleton.class).getPrototype());
    }
}

실행결과

com.giantdwarf.scope.Prototype@1416cf9f
com.giantdwarf.scope.Prototype@1416cf9f

이 문제에서처럼 싱글톤 스코프가 프로토타입의 스코프를 직접 참조하면 안되기 때문에 기존의 빈을 프록시로 감싸고 감싼 프록시를 주입하여 사용하면 된다.

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

실행결과

com.giantdwarf.scope.Prototype@3c0bbc9f
com.giantdwarf.scope.Prototype@1317b708

NO, INTERFACES, TARGET_CLASS가 존재하며 기본값은 NO이다.
INTERFACES의 경우는 JDK dynamic proxy를 사용해서 인터페이스 기반의 프록시를 생성한다.
TARGET_CLASS는 스프링 프레임워크에 내장된 CGLIB를 사용하여 클래스도 클래스 기반의 다이나믹 프록시를 생성한다.