Goal
- ApplicationContext의 빈의 스코프에 대해 알아본다
- 싱글톤 스코프와 프로토타입 스코프에 대해 알아본다
- 프로토 타입 빈이 싱글톤 타입 빈을 참조하는 경우에 대해 알아본다
빈의 스코프
대부분의 경우 싱글톤 스콥이며, 아무런 설정을 하지 않으면 기본 스코프가 싱글톤이다.
싱글톤 스코프
어플리케이션 전반에 걸쳐서 해당 빈의 인스턴스가 1개인 것
@Component
public class Proto {
}
@Component
public class Single {
@Autowired
Proto proto;
public Proto getProto() {
return proto;
}
}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Proto proto;
@Autowired
Single single;
@Override
public void run(ApplicationArguments args) throws Exception {
// AppRunner가 주입받은 proto
System.out.println(proto);
// Single이 참조하고 있는 proto
System.out.println(single.getProto());
}
}
결과
com.example.demosping51.Proto@630b6190
com.example.demosping51.Proto@630b6190
=> 동일한 인스턴스
문제
해당 인스턴스를 어떤 스코프에 따라 새로 만들어야 하는 경우, 스콥을 변경해야 한다면?
해결
프로토타입 스코프를 사용 한다.
프로토타입 스코프
매번 새로운 인스턴스를 만들어 사용하는 스코프. (Request, Session, WebSocket과 유사)
@Scope("prototype")
@Component @Scope("prototype")
public class Proto {
}
@Component
public class AppRunner implements ApplicationRunner {
@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-----
com.example.demosping51.Proto@1386313f
com.example.demosping51.Proto@5e922647
com.example.demosping51.Proto@433c6abb
---single-----
com.example.demosping51.Single@288f173f
com.example.demosping51.Single@288f173f
com.example.demosping51.Single@288f173f
=> proto는 매번 다른 인스턴스
프로토타입 빈이 싱글톤 타입 빈을 참조하면 ?
@Component @Scope("prototype")
public class Proto {
@Autowired
Single single;
}
single이라는 인스턴스는 의도한대로 싱클톤 스콥이기 때문에 매번 같은 인스턴스이기 때문에 문제가 되지 않는다.
프로토타입의 빈은 새롭지만 프로토타입의 빈이 참조하는 싱글톤 스콥의 빈은 항상 동일하다.
싱글톤 스콥의 빈이 프로토타입의 빈을 사용하는 반대의 경우
@Component @Scope("prototype")
public class Proto {
}
- @Autowired Single single; 지워야함. 그렇지 않으면 순환 참조 일어남
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
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 by single-----
com.example.demosping51.Proto@672a1c62
com.example.demosping51.Proto@672a1c62
com.example.demosping51.Proto@672a1c62
문제
싱글톤 스콥이기 때문에 인스턴스가 한번만 만들어지고, 만들어질 때 프로토타입의 프로퍼티도 이미 세팅이 되어있다.
그렇기 때문에 싱글톤 스콥의 빈을 사용할 때, 프로토타입 스콥의 프로퍼티가 변경되지 않는 점이 문제가 된다
해결
1. 프록시 모드
@Component @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Proto {
}
- proxyMode = ScopedProxyMode.DEFAULT - 프록시 사용 x
- proxyMode = ScopedProxyMode.TARGET_CLASS - cg라이브러리를 사용한 다이나믹 프록시로 적용됨
- 다른 빈들을 사용할 때, 해당 빈을 클래스 기반의 프록시 빈을 사용하게 하라
- proxyMode = ScopedProxyMode.INTERFACES
- 다른 빈들을 사용할 때 해당 빈을 인터페이스 기반의 프록시 빈을 사용하게 하라
결과
---proto by single-----
com.example.demosping51.Proto@63d5874f
com.example.demosping51.Proto@60c73e58
com.example.demosping51.Proto@5984feef
왜 프록시로 감싸야 하는가?
Single이 Proto 타입의 빈을 직접 참조하면 안되고 프록시를 거쳐서 참조해야 한다.
프록시를 거쳐야하는 이유는, 직접 참조하면 Proto 타입 인스턴스를 매번 바꿔줘야 하는데, 바꿀 수가 없기 때문이다.
그렇기 때문에 Proxy 인스턴스가 빈으로 등록이 되고, 실질적으로 Proxy 빈을 주입을 해주는 것이다.
Proxy 빈도 Proto 타입의 빈을 상속 받아 만들었기 때문에 타입은 같다. 그래서 주입이 가능하다.
2. Object Provider
@Component @Scope("prototype")
public class Proto {
}
@Component
public class Single {
@Autowired
private ObjectProvider<Proto> proto;
public Proto getProto() {
return proto.getIfAvailable();
}
}
결과
---proto by single-----
com.example.demosping51.Proto@532e27ab
com.example.demosping51.Proto@5f95f1e1
com.example.demosping51.Proto@459b6c53
=> 코드 자체에 스프링 코드가 들어가기 때문에 이 방법은 추천하지 않는다. 최대한 POJO 스럽게 작성하는 것을 권장.
싱글톤 객체 사용시 주의점
- 프로퍼티가 공유됨 - thread unsafe할 경우를 대비하여 코딩해야함
- 모든 싱글톤 스콥의 빈들은 기본값이 ApplicationContext 초기 구동시 인스턴스 생성이 된다
참고
스프링입문 강의 by 백기선
https://www.inflearn.com/course/lecture?courseSlug=spring&unitId=15538 https://www.inflearn.com/course/spring_revised_edition/dashboard
https://www.inflearn.com/course/spring_revised_edition/dashboard
'Spring > about spring' 카테고리의 다른 글
[IoC 컨테이너와 빈] MessageSource (0) | 2023.07.24 |
---|---|
[IoC 컨테이너와 빈] Environment 프로파일, 프로퍼티 (0) | 2023.07.23 |
[IoC 컨테이너와 빈] @Component Scan (0) | 2023.07.22 |
[IoC 컨테이너와 빈] @Autowired (0) | 2023.07.22 |
[IoC 컨테이너와 빈] ApplicationContext (0) | 2023.07.19 |