직접 생성한 DaoFactory 오브젝트 출력 시
DaoFactory factory = new DaoFactory();
UserDao dao1 = factory.userDao();
UserDao dao2 = factory.userDao();
System.out.println(dao1);
System.out.println(dao2);
springbook.dao.UserDao@118f375
springbook.dao.UserDao@117a8bd
userDao를 계속 호출하면 매번 새로운 오브젝트가 생성된다는 것을 알 수 있다.
여기에서 스프링 애플리케이션 컨텍스트에 DaoFactory를 설정 정보로 등록 후 getBean() 메소드를 이용해 userDao라는 이름으로 등록된 오브젝트를 가져오면 다른 결과가 나오는 것을 볼 수 있다.
스프링 애플리케이션 컨텍스트에 등록 후 getBean()를 이용한 출력
ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao3 = context.getBean("userDao", UserDao.class);
UserDao dao4 = context.getBean("userDao", UserDao.class);
System.out.println(dao3);
System.out.println(dao4);
springbook.dao.UserDao@ee22f7
springbook.dao.UserDao@ee22f7
두 오브젝트의 출력 값이 같은 것을 보아, getBean()을 두 번 호출해서 가져온 오브젝트가 동일하다는 사실을 알 수 있다.
차이점
스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 반환.
싱글톤 레지스트리로서의 애플리케이션 컨텍스트
애플리케이션 컨텍스트는 IoC컨테이너이면서, 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이다.
스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
서버 애플리케이션과 싱글톤
스프링은 주로 적용되는 대상이 자바 엔터프라이즈 기술을 사용하는 서버 환경이기 때문에 싱글톤으로 빈을 제작한다.
이유
매번 클라이언트의 요청이 올 때마다 각 로직을 담당하는 오브젝트를 생성하게 된다면, 요청 한 번에 5개 오브젝트 생성, 초당 500개의 요청 시 초당 2500개의 오브젝트 생성, 1분이면 십오만 개, 한 시간이면 9백만 개의 오브젝트가 생성된다.
자바 오브젝트 생성과 가비지 컬렉션의 성능이 좋아지더라도, 부하가 걸리면 서버가 감당하기 힘들기 때문.
그래서 서블릿 클래스당 하나의 오브젝트만 만들고, 사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 공유해 동시에 사용한다.
싱글톤 패턴의 한계
자바에서 싱글톤을 구현하는 보통의 방법
- 클래스 밖에서는 오브젝트를 생성하지 못하도록 생성자를 private으로 만든다.
- 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 필드를 정의
- 스태틱 팩토리 메서드인 getInstance()를 만들고 이 메서드가 최초로 호출되는 시점에서 한 번만 오브젝트가 만들어지게 한다. 생성된 오브젝트는 스태틱 필드에 저장된다. 또는 스태틱 필드의 초기값으로 오브젝트를 미리 만들어둘 수도 있다.
- 한번 오브젝트(싱글톤)가 만들어지고 난 후에는 getInstance() 메서드를 통해 이미 만들어져 스태틱 필드에 저장해둔 오브젝트를 넘겨준다.
public class UserDao {
private static UserDao INSTANCE ;
...
private UserDao (ConnectionMaker connectionMaker) {
this .connectionMaker = connectionMaker;
}
public static synchronized UserDao getlnstance() {
if(INSTANCE == null) INSTANCE = new UserDao(???);
return INSTANCE;
}
...
}
싱글톤을 위한 코드가 추가되고 나니 코드가 상당히 지저분해졌다.
private으로 바뀐 생성자는 외부에서 호출할 수 없기 때문에 DaoFactory에서 UserDao를 생성하며 ConnectionMaker 오브젝트를 넣어주는 것도 불가능해졌다.
일반적인 싱글톤 패턴 구현 방식의 문제
private 생성자를 갖고 있기 때문에 상속이 불가
- priavet생성자를 가진 클래스는 다른 생성자가 없다면 상속이 불가능
- 애플리케이션의 로직을 담고 있는 일반 오브젝트의 경우 싱글톤으로 만들었을 때 객체지향적인 설계의 장점을 적용하기가 어렵다.
- 상속과 다형성 같은 객체지향의 특징이 적용되지 않는 스태틱 필드와 메소드를 사용하는 것도 문제
테스트하기 어렵다.
- 만들어지는 방식이 제한적이기 때문에 테스트에서 사용될 때 목 오브젝트 등으로 대체하기가 어렵다.
- 싱글톤은 초기화 과정에서 생성자 등을 통해 사용할 오브젝트를 다이나믹하게 주입하기 어렵기 때문에 필요한 오브젝트는 직접 오브젝트를 만들어 사용해야 한다. 이런 경우 테스트용 오브젝트로 대체하기 어려움.
서버 환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
- 서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글톤 클래스임에도 하나 이상의 오브젝트가 만들어질 수 있다.
- . 여러 개의 JVM에 분산돼서 설치가 되는 경우에도 각각 독립적으로 오브젝트가 생기기 때문에 싱글톤으로서의 가치가 떨어진다.
싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다
- 싱글톤의 스태틱 메서드를 이용하여 언제든지 싱글톤에 쉽게 접근할 수 있기 때문에 애플리케이션 어디서든지 사용될 수 있고, 그러다 보면 자연스럽게 전역 상태(global state)로 사용되기 쉽다.
싱글톤 레지스트리
자바의 기본적인 싱글톤 패턴의 구현 방식은 여러 가지 단점이 있기 때문에, 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다.
스프링 컨테이너는 싱글톤을 생성하고, 관리하고, 공급하는 싱글톤 관리 컨테이너이기도 하다.
장점
- 스태틱 메서드와 private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글톤으로 활용
- 평범한 자바 클래스라도 IoC방식의 컨테이너를 사용해서 생성과 관계 설정, 사용 등에 대한 제어권을 컨테이너에 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리된다.
가장 중요한 것은 싱글톤 패턴과 달리 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴(싱글톤 패턴은 제외)등을 적용하는 데 아무런 제약이 없다.
주의점
기본적으로 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 시용되는 경우에는 상태 정보를 내부에 갖고 있지 않은 무상태(stateless) 방식으로 만들어져야 한다.
상태가 없는 방식으로 클래스를 만드는 경우, 요청 정보, DB, 서버의 리소스로부터 생성한 정보는 어떻게 다뤄야 하는가?
-> 파라미터와 로컬 변수, 리턴 값 등을 이용한다.
스프링 빈의 스코프
스프링이 관리하는 오브젝트, 빈이 생성되고, 존재하고, 적용되는 범위를 스코프라고 한다.
스프링 빈의 기본 스코프는 싱글톤이다.
경우에 따른 싱글톤 외의 스코프
- 프로토타입 스코프 : 컨테이너 빈을 요청할 때마다 매번 새로운 오브젝트 생성
- 요청 스코프 : HTTP 요청이 생길 때마다 생성
- 세션 스코프 : 웹의 세션 스코프와 유사
등등
'Server > Spring&Spring Boot' 카테고리의 다른 글
[Spring Security] 기본 유저, 비밀번호 변경 (0) | 2022.11.19 |
---|---|
[테스트 코드] Controller Test, MockMVC (0) | 2022.11.18 |
[스스로 이해해보는 스프링] 책임의 분리 (0) | 2022.10.29 |
[토비의 스프링] 스프링의 IoC - 애플리케이션 컨텍스트의 동작 방식 (0) | 2022.10.29 |
[토비의 스프링] 스프링의 IoC - 오브젝트 팩토리를 이용한 스프링 IoC (0) | 2022.10.29 |