일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 자바
- 다이나믹프로그래밍
- 그리드
- AWS
- 브루트포스
- 분할 정복
- GIT
- github action
- BFS
- 재귀
- 백준
- CI/CD
- 그리드 알고리즘
- Spring
- 그래프
- SQL
- 알고리즘
- 역방향 반복자
- 트리
- 스프링
- HTTP
- 분할정복
- 컴퓨터 네트워크
- 도커
- 자료구조
- 순열
- 이분탐색
- dfs
- 다이나믹 프로그래밍
- TCP
- Today
- Total
코딩성장스토리
스프링 싱글톤 본문
이 글은 싱글톤에 대해 공부를 하면서 주의할점과 싱글톤의 좋은 점을 적는다.
천천히 읽어보자
전에 쓴 글처럼 DI를 이용해서
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
@Bean
public OrderServiceImpl orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
}
@Bean
public DiscountPolicy discountPolicy(){
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
이렇게 만들었다.
여기서 문제점이 있다.
그건 바로 클라이언트가 memberService()를 요청할 때마다 객체가 새로 생성이 된다는 것이다.
아래는 예제코드이다.
@Test
@DisplayName("스프링 없는 순수한 DI컨테이너")
void pureContainer(){
AppConfig appConfig= new AppConfig();
//1.조회 : 호출할 떄마다 객체를 생성
MemberService memberService1 = appConfig.memberService(); //hello.core.member.MemberServiceImpl@7530ad9c
//2.조회 : 호출할 떄마다 객체를 생성
MemberService memberService2 = appConfig.memberService(); //hello.core.member.MemberServiceImpl@58a9760d
//참조값이 다른 것을 확인
System.out.println("memberService1 = " + memberService1+"memberService2 = " + memberService2);
assertThat(memberService1).isNotSameAs(memberService2);
}
주석이 주소를 적은 것인데 객체가 다른 것을 확인 할 수 있다.
그래서 여기서 쓰는게 싱글톤이라는 것이다.
싱글톤은 같은객체를 계속 가리키게 해준다.
코드를 봐보자
public class SingletonService {
//static영역에 객체를 1개만 생성한다.
private static final SingletonService instance= new SingletonService();
//public으로 열어서 이 객체가 필요하면 오직 이 static 메서드로만 조회하도록 허용
public static SingletonService getInstance(){
return instance;
}
//생성자를 private으로 선언에서 외부에서 new 키워드를 사용한 객체 생성 못하게 막는다.
private SingletonService(){
}
@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
void singletonServiceTest(){
SingletonService singletonService1 = SingletonService.getInstance(); //hello.core.singleton.SingletonService@6a2f6f80
SingletonService singletonService2 = SingletonService.getInstance(); //hello.core.singleton.SingletonService@6a2f6f80
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
assertThat(singletonService1).isSameAs(singletonService2);
//Same 인스턴스
//equal
}
이 코드에서 싱글톤 적용을 해서 같은 주소를 가리키게 할수 있다.
하지만 이렇게 싱글톤이라는 클래스를 계속 만들면 문제점이 있다.
1. 싱글톤 클래스에 있는 구현체를 사용하기 때문에 의존성에 위반된다.
2.코드가 너무 길어지고 유연하지 않게 된다.
여기서 스프링의 위대함이 나온다. 전에 했던 것처럼 컨테이너에 스프링빈으로 저장을 해두면 싱글톤을 이용하지 않아도 스프링 빈에 있는 객체를 꺼내 쓰기 때문에 싱글톤이 지켜지며 코드도 깔끔해진다.
밑에가 스프링 빈을 이용한 것이다.
@Test
@DisplayName("스프링 빈을 적용한 객체 사용")
void springcontainer(){
// AppConfig appConfig= new AppConfig();
ApplicationContext ac =new AnnotationConfigApplicationContext(AppConfig.class);
//1.조회 : 호출할 떄마다 객체를 생성
MemberService memberService1 = ac.getBean("memberService",MemberService.class); //hello.core.member.MemberServiceImpl@54e22bdd
//2.조회 : 호출할 떄마다 객체를 생성
MemberService memberService2 = ac.getBean("memberService",MemberService.class); //hello.core.member.MemberServiceImpl@54e22bdd
//참조값이 다른 것을 확인
System.out.println("memberService1 = " + memberService1+"memberService2 = " + memberService2);
assertThat(memberService1).isSameAs(memberService2);
}
하지만 여기서 싱글톤의 문제점이 있다.
싱글톤은 객체를 하나만 가리키며 메모리 사용에 유용하지만
클라이언트가 여러명에서 조회할시 똑같은 객체를 조회한다는 것이다.
즉 클라이언트a가 값을 조회해도 클라이언트b의 값이 나올수 있다는 것이다.
밑에 예제가 있다.
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;
}
}
@Test
void statefulServiceSingleton(){
ApplicationContext ac= new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1= ac.getBean(StatefulService.class);
StatefulService statefulService2= ac.getBean(StatefulService.class);
//ThreadA :a사용자가 1000원 주문
statefulService1.order("userA",1000);
//ThreadB:b사용자가 2000원 주문
statefulService2.order("userB",2000);
//ThreadA가 주문 조회
int price = statefulService1.getPrice();
System.out.println("price = " + price);
assertThat(statefulService1.getPrice()).isEqualTo(2000); //true
}
언제나 이런 문제점이 있듯이 해결방법도 있다.
그건 간단하게 그냥 클래스내의 변수를 없애버리는 것이다.
그리고 함수를 int형 반환값으로 만들어버리고 값을 반환하게 만든다.
지역변수로 값을 받는다
코드를 보자
package hello.core.singleton;
public class StatefulService {
//private int price;
public int order(String name,int price){
System.out.println("name = " + name+" price = "+price);
//this.price=price;//여기가 문제
return price;
}
//public int getPrice(){
//return price;
//}
}
void statefulServiceSingleton(){
ApplicationContext ac= new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1= ac.getBean(StatefulService.class);
StatefulService statefulService2= ac.getBean(StatefulService.class);
//ThreadA :a사용자가 1000원 주문
int pricea=statefulService1.order("userA",1000);
//ThreadB:b사용자가 2000원 주문
int priceb= statefulService2.order("userB",2000);
//ThreadA가 주문 조회
// int price = statefulService1.getPrice();
System.out.println("price = " + pricea);
//assertThat(statefulService1.getPrice()).isEqualTo(2000);
}
스프링 빈을 쓸 때 주의할점
@Configuration 을 꼭 써야한다
써야하는 이유는 똑같은 클래스가 다른 객체로 스프링 빈에 저장이 될수 있다.
위에서 봤던 appconfig를 보자
package hello.core;
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
@Bean
public OrderServiceImpl orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
}
@Bean
public DiscountPolicy discountPolicy(){
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
여기서 memberService -> memorymemberRepository 객체를 생성하고
orderSerrvice-> memorymemberRepository 객체를 생성한다
즉 같은 클래스 임에도 2개의 객체가 스프링빈에 저장이 되는 것이다.
이를 해결하기 위해 @Configuration이 필요하다
이 복잡한 로직으로 인해 같은 클래스라고 생각이 되면 스프링빈에서 똑같은 객체로 반환을 해준다.
'백 엔드 > spring' 카테고리의 다른 글
스프링 빈 스코프 (0) | 2022.02.20 |
---|---|
스프링 의존관계 자동 주입 (0) | 2022.02.17 |
스프링 컨포넌트스캔 (0) | 2022.02.15 |
스프링 IOC,DI,컨테이너 (0) | 2022.02.14 |
spring 도전기 (1) | 2021.12.08 |