[Spring] 싱글톤 컨테이너
2023. 6. 11. 18:44
Section 3 들어가기 전 한 Section 1에서 실습했던 자바 프로젝트를 스프링으로 전환하는 연습 중.
스프링에서는 객체 인스턴스를 싱글톤으로 자체적으로 관리한다.
✔️ 코드를 통해 살펴보기
➡️ 본격적으로 알아보기 전에 먼저 싱글톤 패턴이 무엇인지 간단하게 알아보자.
❓싱글톤 패턴
→ 클래스의 인스턴스가 딱 한 개만 생성되는 것을 보장하는 디자인 패턴
즉, 생성자를 private 접근 제한해서 외부에서 new 연산자로 생성자를 호출할 수 없도록 막는 것이다.
public class Singleton {
// (1) static 영역에 필드 선언과 초기화 (한 개)
private static Singleton singleton = new Singleton();
// (2) private 접근 권한을 갖는 생성자 선언해서 외부에서 new 키워드 사용한 객체 생성을 하지 못하게 막는다.
private Singleton() {
}
// (3) 객체 인스턴스가 필요하다면 이 메서드를 통해 조회 가능하다.
public static Singleton getInstance() {
return singleton;
}
1. 이전 코드에서의 싱글톤 패턴
public class Main {
public static void main(String[] args) {
AppConfigurer appConfigurer = new AppConfigurer();
OrderApp orderApp = new OrderApp(
appConfigurer.productRepository(),
appConfigurer.menu(),
appConfigurer.cart(),
appConfigurer.order()
);
orderApp.start();
}
}
public class AppConfigurer {
public ProductRepository productRepository() {
return new ProductRepository();
}
public Menu menu() {
return new Menu(productRepository().getAllProducts());
}
public Cart cart() {
return new Cart(productRepository(), menu());
}
public Discount discount() {
return new Discount(
new DiscountCondition[]{
new CozDiscountCondition(new FixedRateDiscountPolicy(10)),
new KidDiscountCondition(new FixedAmountDiscountPolicy(500))
});
}
public Order order() {
return new Order(cart(), discount()); // 📍
}
}
- 위 코드를 보면 📍 표시된 order() 메서드에서 인스턴스를 생성하면서 cart() 를 호출하고 있다. 이렇게 되면 Main에서 cart()를 통해 만든 Cart 인스턴스와 AppConfigurer의 order() 내 cart()를 통해 만들어진 Cart 인스턴스(📍)는 주소값이 다른 별개의 인스턴스가 되는 문제가 생긴다.
→ 실제로 주문하기를 눌렀을 때 장바구니에 담긴 상품 목록이 안 보이는 문제가 생겼다. 위에서 설명한 것과 같이 장바구니 목록의 인스턴스 주소값과 주문하기 목록의 인스턴스 주소값이 다르기 때문이다. 그러니 당연히 안에 담긴 값들이 다를 수 밖에 없다. - 그렇다면 해결 방안은 Cart의 인스턴스가 단 한 번만 만들어지도록 하여 공유할 수 있도록 해야한다.
public class AppConfigurer {
private Cart cart = new Cart(productRepository(), menu()); // 📍 추가
public ProductRepository productRepository() {
return new ProductRepository();
}
public Menu menu() {
return new Menu(productRepository().getAllProducts());
}
// 📍 추가
public Cart cart() {
return cart;
}
public Discount discount() {
return new Discount(
new DiscountCondition[]{
new CozDiscountCondition(new FixedRateDiscountPolicy(10)),
new KidDiscountCondition(new FixedAmountDiscountPolicy(500))
}
);
}
public Order order() {
return new Order(cart(), discount());
- 인스턴스가 단 한 번만 생성될 수 있도록 AppConfigurer에 cart 필드를 정의하고 초기화를 하고 cart() 메서드가 리턴값으로 cart를 주면 이제는 주소값이 동일한 Cart 인스턴스가 리턴되도록 보장할 수 있게 된다.
- 만약 여러 개의 동시다발적인 고객 요청을 처리해야 하는 웹 애플리케이션 요청이 있을 때마다 최초에 단 하나의 객체를 생성해 두고 요청이 돌아올 때마다 같은 객체를 공유한는 방법으로 메모리 낭비를 최소화할 수 있다.
- 이러한 장점도 있지만 단점도 있다. 그렇기 때문에 꼭 필요한 경우가 아니면 사용을 지양하는 것이 좋다.
- 싱글톤 패턴의 구현코드 자체가 많이 들어간다
- 클라이언트가 구체 클래스에 의존한다. → DIP 위반
- 클라이언트가 구체 클래스에 의존하여 OCP를 위반할 가능성이 높다.
- 코드 유연성이 많이 떨어진다.
2. 싱글톤 컨테이너
➡️ 스프링 컨테이너는 싱글톤 패턴 코드를 직접 작성하지 않아도 내부적으로 객체 인스턴스를 싱글톤으로 관리함으로써 싱글톤 패턴이 가지는 단점을 효과적으로 극복할 수 있다.
➡️ 이전 게시글에서 스프링 전환 후의 AppConfigurer 클래스를 볼 수 있는데 여기는 싱글톤 패턴에 대한 코드가 생략되고 메서드 호출 시 빈 객체를 생성하는 다른 메서드들과 같은 모양을 하고 있는 것을 알 수 있다.
➡️ 출력 결과를 보면 스프링 컨테이너가 내부적으로 객체들을 싱글톤으로 관리한다는 사실을 알 수 있다.
➡️ 또한 스프링 컨테이너는 싱글톤 컨테이너 역할을 수행한다.
: 싱글톤 레지스트리(Singleton Registry)
→ 싱글톤으로 객체를 생성 및 관리하는 기능
→ 스프링은 CGLB라는 바이트코드 조작 라이브러리를 사용하여 싱글톤 레지스트리를 가능하게 한다.
'Spring > 기본' 카테고리의 다른 글
[Spring] @Configuration, @Component (0) | 2023.06.21 |
---|---|
[Spring] 스프링 컨테이너와 빈 (0) | 2023.06.11 |
[Spring] AppConfigurer 클래스의 역할 (0) | 2023.06.11 |
[Spring] 스프링 프레임워크 주요 모듈 알아보기 (0) | 2023.05.31 |