[스프링 프레임워크 핵심기술] Bean의 scope

Dayeon myeong·2021년 3월 17일
0

Bean의 scope

IoC 강의 정리 에서 빈의 스코프에 대해 잠깐 다뤘었다.

빈의 기본 값이 싱글톤 스코프이기 때문에 일반적으로 사용하는 Bean은 모두 싱글톤 스코프의 Bean이다.

싱글톤 스코프이란, 어플리케이션 전반에 사용되는 해당 Bean의 인스턴스가 하나 뿐이라는 것이다.
(실제로 생성되는 객체는 하나이고, 최초 생성 이후에 이 객체를 공유해서 사용한다.)

@Component
public class Single {

  @Autowired
  Proto proto;

  public Proto getProto() {
    return proto;
  }

}
@Component
public class Proto {
}

만약 Proto, Single이란 두개의 빈이 있을 때 아무 설정을 하지 않았으니 싱글톤 스코프를 가진다.


@Component
public class AppRunner implements ApplicationRunner {

  @Autowired
  Single single;

  @Autowired
  Proto proto;

  @Override
  public void run(ApplicationArguments args) throws Exception {
    
    System.out.println(proto); //이것과
    System.out.println(single.getProto()); //이것은 동일 객체
    
  }
}

싱글톤이기 때문에 둘 다 똑같은 Proto가 찍힌다.

반면, 프로토타입은 매번 새로운 인스턴스를 만들어서 사용하는 것이다.

일반적으로 프로토타입으로 scope를 바꾸기 위해선
@Scope("prototype") 어노테이션을 설정하여 사용 가능하다.

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

 }

위와 같이 설정하면 매번 다른 Proto 인스턴스를 생성한다.

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

Proto라는 Bean은 매번 다른 인스턴스로 생성되지만, 참조하고 있는 Single은 매번 같은 인스턴스이므로 아무 문제가 없다.

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

  @Autowired
  Single single;
}

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

이 경우에는 문제가 될 수 있다. Single이라는 Bean은 싱글톤 스코프이므로 인스턴스가 한 번만 생성된다. 이 때 참조하고 있는 Proto 빈도 같이 세팅이 된다. 따라서 이 경우는 Proto가 프로토타입 스코프임에도 불구하고 Single이 참조하고 있는 Proto의 값이 변경되지 않는다.

@Component
public class Single {

  @Autowired
  Proto proto;

  public Proto getProto() {
    return proto;
  }

}

해결 방법
이러한 문제를 해결하기 위해서 여러 방법이 있다.

  • ProxyMode 설정
@Component @Scope(value = "prototype" , proxyMode = "ScopeProxyMode.TARGET_CLASS")
public class Proto {

  @Autowired
  Single single;
}

proxymode를 설정하여 Proto라는 빈을 Proxy로 감싸라고 알려준다.
그러면 다른 Bean들이 proto를 사용할 때, 이 bean을 감싸고 있는 proxy bean을 사용하게 된다.

Single 빈은 Proto를 직접 참조하지 않고 proxy를 거쳐서 사용이 되기 때문에 Proto는 매번 다른 인스턴스를 생성할 수 있게 된다.

즉, proxy bean이 bean으로 등록되고, 주입도 역시 proxy bean을 해준다. (proxy는 타입이 같기 때문에 주입해 줄 수 있다.)

  • Object Provider 사용
@Component
public class Single {

  @Autowired
  ObjectProvider<Proto> proto;

  public Proto getProto() {
    return proto.getIfAvailable();
  }

}

참조하는 곳에 ObjectProvider를 사용하여 프로토타입의 빈을 세팅해주면 proto 인스턴스가 업데이트 가능하다.

싱글톤 객체 사용시 주의할 점

  • 싱글톤 인스턴스 생성시 여러 곳에서 해당하는 하나의 인스턴스를 공유하게 된다. 그 객체 내부에 변수가 존재할 경우, 여러 곳에서 그 값을 고쳐버리면(멀티스레드) 안전하지 못하다 (=스레드 세이프하지 않다)
@Component
public class Single {

  @Autowired
  Proto proto;
  
  int number = 0; // 변수

  public Proto getProto() {
    return proto;
  }

}
profile
부족함을 당당히 마주하는 용기

0개의 댓글