You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
우리가 만들었던 스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때 마다 객체를 새로 생성한다.
고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸된다! -> 메모리 낭비가 심하다. , GC가 계속 작동되는 등의 문제가 발생
해결방안은 해당 객체가 딱 1개만 생성되고, 공유하도록 설계하면 된다. -> 싱글톤 패턴
싱글톤 패턴
클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.
그래서 똑같은 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.
private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.
싱글톤 패턴을 적용한 예제코드를 보자. main이 아닌 test 위치에 생성하자.
main에 영향을 안주고 test로 만들기 위해서 test로 만듦
packagehello.core.singleton;
publicclassSingletonService {
privatestaticfinalSingletonServiceinstance = newSingletonService(); // static 영역에 class 레벨에 올라가 1개만 사용할 수 있다publicstaticSingletonServicegetInstance() {
returninstance; //조회시 사용
}
privateSingletonService() {
// 이 클래스에서만 사용할 수 있게 만들고
}
publicvoidlogic() {
System.out.println("싱글톤 객체 로직 호출 ");
}
}
static 영역에 객체 instance 를 미리 하나 생성해서 올려둔다.
이 객체 인스턴스가 필요하면 오직 getInstance() 메서드를 통해서만 조회할 수 있다. 이 메서드를 호출하면 항상 같은 인스턴스를 반환한다.
딱 1개의 객체 인스턴스만 존재해야 하므로 생성자를 private 으로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.
** 컴파일 오류만 나는게 제일 좋은 오류
이유는 static method에 @bean을 사용하게 되면 싱글톤 보장을 위한 지원을 받지 못한다.
'static 메서드는 proxy가 적용되지 않기 때문에 사용하면 안된다.
싱글톤을 보장받으려면 동일한 빈의 존재여부를 확인해서 받는다.
proxy로 인해 @bean이 붙은 메서드를 호출하면 컨테이너에 동일한 빈이 존재하는지 확인하게 된다. 그러나 proxy가 적용되지 않는 static 메소드는 컨테이너에 동일한 빈이 존재하는지에 대한 로직이 수행되지 않기 때문에 싱글톤을 보장하지 않는다'
'static 메소드는 특정 인스턴스에 속하지 않기 때문에 스프링 컨테이너가 빈을 관리하는 방식, 즉 인스턴스 레벨에서 처리하는 라이프사이클 관리, 의존성 주입 및 프록시 적용 등과 같은 과정을 거치기 어렵습니다.`
순수한 클래스라면 다음 밑에와 같이 출력되어야 한다. class hello.core.AppConfig
그런데 예상과는 다르게 클래스 명에 xxxCGLIB가 붙으면서 상당히 복잡해진 것을 볼 수 있다. 이것은 내가
만든 클래스가 아니라 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 AppConfig
클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
조작 라이브러리로 상속받아서 다른 클래스를 만든것이다.
AppConfig@CGLIB ~~~가 다른 임의의 클래스가 싱글톤이 보장되도록 해준다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
웹애플리케이션과 싱글톤
스프링 없는 순수한 DI 컨테이너 테스트
싱글톤 패턴
싱글톤 패턴을 적용한 예제코드를 보자. main이 아닌 test 위치에 생성하자.
main에 영향을 안주고 test로 만들기 위해서 test로 만듦
getInstance()메서드를 통해서만 조회할 수 있다. 이 메서드를 호출하면 항상 같은 인스턴스를 반환한다.** 컴파일 오류만 나는게 제일 좋은 오류
Test
싱글톤 패턴을 적용하면 고객의 요청이 올때 마다 객체를 생성하는 것이 아니라 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다. 하지만 싱글톤 패턴은 다음과 같은 수많은 문제점을 가지고 있다.
싱글톤 패턴의 문제점
기본적으로 이부분은 들어가야 한다.
싱글톤 컨테이너 = 스프링 컨테이너
스프링 컨테이너를 사용하는 테스트 코드
싱글톤 방식의 주의점
싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
무상태(stateless)로 설계해야 한다
스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다!!
StatefulService의price필드는 공유되는 필드인데, 특정 클라이언트가 값을 변경한다.**무상태로 설계하는 방법
@configuration과 싱글톤
@bean memberService -> new MemoryMemberRepository()
@bean orderService -> new MemoryMemberRepository()
이럼 싱글톤이 깨지는 건가? 하고 고민이 생겨야 함
결과적으로 각각의 다른 2개의 MemoryMemberRepository 가 생성되면서 싱글톤이 깨지는 것처럼 보인다. 스프링 컨테이너는 이 문제를 어떻게 해결할까?
실험을 하기 >> 직접 테스트 하기
MemberServiceImpl에 있는 MemberRepository의 실제 값을 확인하기
위 값을 MemberServiceImpl, OrderServiceImpl에 넣기
결과

처음 결과값이 달라서 찾아본 결과, static 변수 때문에 발생하는 오류였습니다.
이유는 static method에 @bean을 사용하게 되면 싱글톤 보장을 위한 지원을 받지 못한다.
'static 메서드는 proxy가 적용되지 않기 때문에 사용하면 안된다.
싱글톤을 보장받으려면 동일한 빈의 존재여부를 확인해서 받는다.
proxy로 인해 @bean이 붙은 메서드를 호출하면 컨테이너에 동일한 빈이 존재하는지 확인하게 된다. 그러나 proxy가 적용되지 않는 static 메소드는 컨테이너에 동일한 빈이 존재하는지에 대한 로직이 수행되지 않기 때문에 싱글톤을 보장하지 않는다'
'static 메소드는 특정 인스턴스에 속하지 않기 때문에 스프링 컨테이너가 빈을 관리하는 방식, 즉 인스턴스 레벨에서 처리하는 라이프사이클 관리, 의존성 주입 및 프록시 적용 등과 같은 과정을 거치기 어렵습니다.`
결과

new MemoryMemberRepository호출해서 다른 인스턴스가 생성되어야 하는데 ?call해서 AppConfig가 3번 호출되었다.
Configuration 과 바이트 코드 조작의 마법
예상은 memberRepository가 세번 호출되는 것인데 한번만 호출되었다.
결과
클래스명 $$ EnhancerBySpringCGLIB$$~class hello.core.AppConfig그런데 예상과는 다르게 클래스 명에 xxxCGLIB가 붙으면서 상당히 복잡해진 것을 볼 수 있다. 이것은 내가
만든 클래스가 아니라 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 AppConfig
클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
조작 라이브러리로 상속받아서 다른 클래스를 만든것이다.
AppConfig@CGLIB ~~~가 다른 임의의 클래스가 싱글톤이 보장되도록 해준다.
AppConfig@CGLIB의 예상 코드
@bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.
덕분에 싱글톤이 보장되는 것이다
오버라이딩 된 스프링 컨테이너가 없으면 등록한 걸 호출해준다.
참고 AppConfig@CGLIB는 AppConfig의 자식 타입이므로, AppConfig 타입으로 조회 할 수 있다.
@configuration 을 적용하지 않고, @bean만 적용하면 어떻게 될까?
만약
@Autowired MemberRepository memberRepository;하면 결과가 똑같아지고 test도 통과된다.정리
단축키
Ctrl + Shift + Enter: code completionCtrl+Shift+T: NavigateCtrl + Alt + V: Extract/Introduce(맨마지막 단축키가 잘 안외워져 올려뒀습니다.)
All reactions