스프링 핵심원리-기본편을 듣고 정리한 글입니다.
스프링을 배우신다면 꼭 추천합니다!
웹 애플리케이션과 싱글톤
스프링을 사용하지않고 순수한 Java 코드로 웹 애플리케이션을 만들었다고 가정하자.
웹 애플리케이션을 여러 고객이 동시에 요청을 한다면?
호출할때마다 각각의 다른 객체를 생성한다.
memberService 사용시마다 다른 객체가 만들어지면... 메모리 낭비가 심해진다.
해결방안: 싱글톤
해당 객체가 딱 1개만 생성되고, 그 객체를 공유하도록 설계한다.
싱글톤 패턴
클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
=> private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.
static 영역에 객체 instance를 미리 만들어놓고, 조회시 같은 객체 인스턴스만 반환되도록 한다 => 하나의 객체 인스턴스를 공유
생성자는 private으로 막아둔다.(new 호출하여 객체생성 되지 않도록 함)
정말 같은 객체를 반환하는지 테스트해보자.
테스트
정말 같은 객체를 반환하는 것을 볼 수 있다.
(테스트시에 print로 눈 확인보다는 isSameAs 등등의 함수를 이용하여 비교하는것이 좋다.)
그럼 우리가 스프링으로 만든 코드에 싱글톤을 적용해야하는데.. 그럴 필요가 없다!
스프링 컨테이너가 기본적으로 객체를 싱글톤 패턴으로 관리해주기 때문이다.
싱글톤 단점
이미 만들어진 객체를 공유하여 효율적으로 사용할 수 있다는 장점이 있지만, 단점도 꽤 많다.
그러나 스프링 컨테이너는 이 모든 싱글톤의 단점들을 해결하면서, 객체 인스턴스를 싱글톤으로 관리한다. 😉
=> 싱글톤 코드를 추가하지않아도 되고, DIP, OCP, private, 테스트로부터 자유롭다.
싱글톤 컨테이너
스프링 컨테이너가 관리하는 스프링 빈 = 싱글톤으로 관리되는 빈 이다.
정말 같은 객체를 반환하는지 확인해보면,
* 스프링의 기본 빈 등록방식은 싱글톤이지만 요청할때마다 새로운 객체를 생성해서 반환하는 기능도 제공한다.. 후에 빈 스코프에서 설명!
그러나 99%정도는 싱글톤으로 사용한다.
싱글톤 방식의 주의점
객체 인스턴스를 하나만 생성해서 공유하기때문에 상태를 유지하게(stateful) 설계하면 안된다!
=> 무상태(stateless)로 설계해야한다.
- 특정 클라이언트에 의존되는 필드X, 값 변경 못하게
- 가급적 읽기만 가능하도록, 필드 대신 공유되지않은 지역변수,파라미터,ThreadLocal 사용하기
Stateful한 서비스 만들기 - 공유필드
가격이 저장된다.
테스트
스프링은 싱글톤 방식이므로 고객이 각각 주문해도 객체 인스턴스는 같다.
고객1의 주문값이 고객2의 주문 가격으로 덮어져 대형사고가 난다. (결제금액이 2배!)
이런 경우로 터지는 문제는 실무에서 몇년에 한번씩은 꼭 만난다..
스프링 빈은 무상태로 설계해야한다! (공유필드 주의!)
해결: 필드가 아닌 반환값으로 처리하자
테스트 성공
실무에서 상속관계있고 복잡하면 해결하기 어려우니 꼭 기억하자~
@Configuration과 싱글톤
AppConfig 코드를 살펴보면
서비스 생성시에 MemoryMemberRepository가 계속 new로 계속 생성되는 것을 볼 수 있다.
객체 인스턴스가 계속 생성되면 싱글톤이 깨지는게 아닌가? 라고 생각될 수 있지만 테스트를 해보면
테스트
memberServiceImpl에서 사용되는 것과 orderService에서 사용되는 memberRepository가 같은 인스턴스인지
확인해보자.
memberServiceImpl
repository 리턴하는 코드 추가
OrderServiceImpl
repository 리턴하는 코드 추가
테스트 코드
모두 같은 객체 인스턴스를 사용하는 것을 볼 수 있다.
여러번 호출해도 인스턴스는 계속생성되지 않는 것일까?
AppConfig 수정
생성자 호출시마다 로그가찍히게 만들고 테스트를 돌려보자.
전에 만들었던 ConfigurationSingletonTest 그대로 돌리기
테스트 수행하면 빈이 차례대로 올라간다.
로그값을 보면 3번만 (즉 각각의 빈마다 한번씩만) 생성자가 호출되는 것을 알 수 있다.
한번 생성된 후에는 생성자가 더이상 호출되지않는다 => 스프링 컨테이너는 싱글톤을 완전히 보장하는구나!
어떻게 그게 가능할까?
바이트조작
스프링은 클래스의 바이트코드를 조작하는 라이브러리=CGLIB를 사용한다.
AppConfig 스프링빈을 조회해보면
(첫번째줄에서 AppConfig가 스프링 빈으로 등록된다.)
뒤에 $$부터 해서 CGLIB(바이트조작 라이브러리) 이 붙었다.
이를 통해 스프링 컨테이너가 내가 만든 클래스를 빈으로 등록하는 것이 아니라,
CGLIB을 사용하여 AppConfig(내가만든) 클래스를 상속받은 임의의 다른 클래스를 만들고 그 클래스를 스프링빈으로 등록한 것을 알 수 있다.
이름은 AppConfig지만 바이트 조작 라이브러리를 사용한 다른 클래스를 사용하고 있는 것이다.
이 클래스는 스프링 빈이 존재하면 존재하는 빈을 반환하고,
그렇지 않고 없으면 새로 생성하여 스프링 빈으로 등록하고 반환하는 코드를 실행한다.
=> 싱글톤을 보장한다.
그 외 참고사항
- AppConfig 조회시에 자식타입인 AppConfig@CGLIB이 조회된다.
- @Configuration을 사용하지않으면 싱글톤이 깨져서 안좋다(Autowired로 해결할 수 있지만 안좋다).. 설정에 꼭 쓰자!
'Spring > 스프링 핵심 원리' 카테고리의 다른 글
의존관계 자동주입 (0) | 2023.02.27 |
---|---|
컴포넌트 스캔 (2) | 2023.02.23 |
스프링으로 전환하기, 스프링 컨테이너와 스프링 빈 (0) | 2023.02.13 |
IoC, DI, 컨테이너 (0) | 2023.02.03 |
예제에 객체 지향 원리(SOLID) 적용 (Only java) (0) | 2023.02.03 |