728x90
5.싱글톤 컨테이너
- 목차
- 1.웹 애플리케이션 싱글톤
- 2.싱글톤 패턴
- 3.싱글톤 컨테이너
- 4.싱글톤 방식의 주의점
- 5.@Configuration 과 싱글톤
- 6.@Configuration 과 바이트코드 조작의 마법
1.웹 애플리케이션 싱클톤
- 웹 애플리케이션은 보통 고객이 동시에 요청한다
SingletonTest
public class SingletonTest {
@Test
@DisplayName("스프링이 없는 순수한 DI 컨테이너")
void pureContainer(){
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);
}
}
- 스프링 없는 순수한 DI 컨테이너는 요청할때마다 객체를 생성
- 메모리 낭비가 심하다
- 해당객체를 1개만 생성되고, 공유하도록 설계 한다 - 싱글톤 패턴
2.싱글톤 패턴
- 클래스의 인스턴스가 1개만 생성되는것을 보장한다
- private 생성자를 사용해서 외부에서 임의 new 키워드를 못사용하도록 사용
SingletonService
public class SingletonService {
// 클래스안에 static 가 있으면 하나만 만들어짐
private static final SingletonService instance = new SingletonService();
public static SingletonService getInstance(){
return instance;
}
// private 생성자를 만들어서 new 사용을 방지
private SingletonService(){
}
public void login(){
System.out.println("싱글톤 객체 로직 호출");
}
}
- 객체가 필요하면 getInstance 에서 조회할수 있다
- private 생성자를 만들어서 외부에서 new 키워드로 객체 인스턴스 생성을 막는다
SingletonService
public class SingletonTest {
@Test
@DisplayName("싱글톤 패턴을 적용한 객체")
void singletonServiceTest() {
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
assertThat(singletonService1).isSameAs(singletonService2);
}
}
- isSameAs 을 이용해서 같은 객체를 쓰는지 알수 있다
- 호출할때 마다 같은 객체를 반환한다
싱글톤 패턴의 문제점
- 코드자체가 많이 들어간다
- 구체클래스에 의존한다 - DIP 위반
- 구체클래스에 의존한다 - OCP 위반
- 테스트가 어렵다
- 내부속성 변경하거나 초기화하기 어렵다
- private 생성자 때문에 자식클래스 만들기 어렵다
- 유연성이 많이 떨어진다
3.싱글톤 컨테이너
- 스프링 컨테이너는 싱글톤의 문제점을 해결하면서 인스턴스를 싱글톤으로 관리한다
- 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다
- 지저분한 코드가 안들어간다 , DIP OCP 테스트 private 생성자 등 자유롭게 사용
SingletonTest
public class SingletonTest {
@Test
@DisplayName("스프링 컨테이너와 싱글톤")
void springContainer(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//1.조회
MemberService memberService1 = ac.getBean("memberService",MemberService.class);
//2.조회
MemberService memberService2 = ac.getBean("memberService",MemberService.class);
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
assertThat(memberService1).isSameAs(memberService2);
}
}
- 같은 객체를 반환한다
4.싱글톤 방식의 주의점
- 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤방식은 상태를 유지하게 설계하면 안된다(무상태)
- 특정 클라이언트에 의존적인 필드가 있으면 안된다
- 값을 변경할수 있는 필드가 있으면 안된다
- 가급적 읽기만 사용
- 필드대신에 자바에서 공유되지않는 지역변수,파라미터,스레드로컬에 사용
StatefulService
public class StatefulService {
private int price; //상태유지 필드
public void order(String name,int price){
System.out.println("name = " + name + " price = " + price);
this.price=price; //문제
}
public int getPrice(){
return price;
}
}
- 테스트 용도로 StatefulService 를 만든다
- 간단한 이름과 가격
StatefulServiceTest
class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
///스레드 A
statefulService1.order("userA",10000);
//스레드 B
statefulService2.order("userB",20000);
//사용자 A 금액
int price =statefulService1.getPrice();
System.out.println("price = " + price);
}
static class TestConfig{
@Bean
public StatefulService statefulService(){
return new StatefulService();
}
}
}
- 같은객체를 사용하는 중에서 this.price=price 변경되는 값이 있을때
- statefulService1 의 가격은 20000원이 찍힌다
- statefulService 필드는 공유되는 필드이다
- 공유필드는 조심해야된다. 스프링 빈은 항상 무상태로 설계하자
StatefulService 변경
public class StatefulService {
public int order(String name,int price){
System.out.println("name = " + name + " price = " + price);
return price;
}
}
StatefulServiceTest 변경
class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
///스레드 A
int userA = statefulService1.order("userA", 10000);
//스레드 B
int userB = statefulService2.order("userB", 20000);
//사용자 A 금액
System.out.println("price = " + userA);
}
static class TestConfig{
@Bean
public StatefulService statefulService(){
return new StatefulService();
}
}
}
- 지역변수로 문제를 해결하자
- 항상 무상태로 설계하자
5.@Configuration 과 싱글톤
ConfigurationSingletonTest
public class ConfigurationSingletonTest {
@Test
void configurationTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberRepository1 = " + memberRepository1);
System.out.println("memberRepository2 = " + memberRepository2);
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository1);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository2);
}
}
- AppConfig 에 @Configuration 을 통해서 같은 객체를 공유한다
6.@Configuration 과 바이트코드 조작의 마법
- 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다
ConfigurationSingletonTest
public class ConfigurationSingletonTest {
@Test
void configurationDeep(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean);
}
}
- AppConfig 빈을 출력하면 스프링이 CGLB 라는 바이트코드 조작 라이브러리 사용
- AppConfig 클래스를 상속받은 임의의 다른클래스 생성
- 임의의 다른클래스를 스프링빈으로 등록한다
- @Configuration 이 없어도 동작은되나 CGLB 적용이 안된다 - 싱글톤 x
정리
- @Bean 만 사용하면 싱글톤은 보장을 안한다
- 스프링 설정정보는 항상 @Configuration 을 쓰자
728x90
'Back-End > Spring' 카테고리의 다른 글
7.의존관계 자동주입 (0) | 2021.01.11 |
---|---|
6.컴포넌트 스캔 (0) | 2021.01.07 |
4.스프링 컨테이너와 스프링 빈 (0) | 2021.01.04 |
3.스프링 핵심 원리 이해2 - 객체지향원리적용 (0) | 2021.01.01 |
2.스프링 핵심 원리 이해1 - 예제만들기 (0) | 2020.12.31 |