스프링은 기업용 온라인 서비스를 지원하기위해 탄생한 웹 애플리케이션이다.
스프링 없는 순수한 DI 컨테이너가 가진 문제점을 보고 스프링과 비교
문제점 : 웹 애플리케이션은 보통 고객이 요청을 하면 요청을 할 때 마다 객체를 만들어야 한다
고객이 각기 다른 요청을 2번 요청했을 때
@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContatiner() {
AppConfig appConfig = new AppConfig();
//1. 조회 : 호출할 때 마다 객체를 생성
MemberService memberService1 = appConfig.memberService();
//2. 조회 : 호출할 때 마다 객체를 생성
MemberService memberService2 = appConfig.memberService();
//참조값이 다른 것을 확인
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
assertThat(memberService1).isNotSameAs(memberService2);
}
AppConfig 내에 객체를 포함하면 고객 요청 2개에 총 4개의 객체가 생성된 것이다. -> 메모리 낭비
해결 방법은 해당 객체가 딱 1개만 생성되고, 공유하도록 설계하면 된다. -> 싱글톤 패턴
그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.
package com.example.demo.singleton;
public class SingletonService {
private static final SingletonService instance = new SingletonService();
//내부적으로 자기자신을 생성해서 instance에 참조로 넣어 놓는다.
public static SingletonService getInstance(){
return instance;
}
//인스턴스의 참조를 꺼낼 수 있는 방법은 getInstance 함수밖에 없다.
private SingletonService(){
}
//private 생성자를 쓰면 외부에서 선언할 수 없게 막는다.
public void logic(){
System.out.println("싱글톤 객체 로직 호출");
}
}
생성자를 private으로 선언해 외부에서 한 번 더 생성하지 못하도록 한다.
@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
void singletonServiceTest(){
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
assertThat(singletonService1).isEqualTo(singletonService2);
//same(==) : 진짜 인스턴스끼리 비교하는 느낌
//equal : equals 메서드랑 같은 느낌
}
//객체 하나를 공유해서 사용했기 때문에 참조값도 동일하다.
다만, 스프링은 자체적으로 싱글톤으로 생성하기 때문에 따로 스프링을 사용할 때는 번거롭게 할 필요는 없다.
따라서, 스프링 컨테이너를 싱글톤 컨테이너라고 하기도 한다.
싱글톤 코드는 기존에 가지고 있던 문제를 해결해준다. 다만 코드를 추가로 작성해야하기 때문에 아래와같은 문제점이 생긴다.
싱글톤 단점
OCP 원칙을 위반할 가능성이 높다.private 생성자로 자식 클래스를 만들기 어렵다.유연성이 떨어진다.안티패턴으로 불리기도 한다.