싱글톤 컨테이너
스프링 컨테이너는 싱글톤 패턴의 문제점들을 해결하면서, 객체 인스턴스를 싱글톤으로 생성한다.
스프링에서는 싱글톤 패턴을 구현하기 위한 지저분한 코드가 필요없다.
DIP, OCP , 테스트, private 생성자로 부터 자유로워진다.
스프링 컨테이너를 사용하면 기본적으로 싱글톤으로 사용된다.
싱글톤 방식말고 요청할때마다 새로운 객체를 생성해서 반환하는 기능도 제공한다.
하지만 거의 대부분 99% 싱글톤 방식을 사용한다.
싱글톤 방식의 주의점
싱글톤 패턴이든, 스프링 컨테이너든, 하나만 생성해서 공유하는 싱글톤 방식은, 상태를 유지하게
설계되면 안된다.
- 무상태로 설계해야한다
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기만 가능해야한다.
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// a가 1000원 주문
statefulService1.order("userA", 1000);
// b가 2000원 주문
statefulService2.order("userB", 2000);
// ThreadA: 사용자 A가 주문 금액 조회
int price = statefulService1.getPrice();
System.out.println("price = " + price);
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(1000);
}
위의 코드는 오류가 생기는 코드이다.
왜냐하면, 스프링 컨테이너는 하나의 StatefulService가 생성된다 싱글톤 패턴으로,
ac.getBean으로 선택한 StatefulService는 객체가 두개생성 된 것이 아니라
하나의 객체를 두명이 사용하는게 되어버리고 결국,
a주문을 b주문이 덮어버리기 때문에 1000에 equal하지 않고 2000에 equal이 된다.
@Test
@DisplayName("싱글톤 객체 테스트 successful")
void statefulServiceSingletonResolve() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// a가 1000원 주문
int userA = statefulService1.order("userA", 1000);
// a가 1000원 주문
int userB = statefulService2.order("userB", 2000);
// ThreadA: 사용자 A가 주문 금액 조회
System.out.println("price = " + userA);
Assertions.assertThat(userA).isEqualTo(1000);
}
위 코드는 수정된 로직이며 서비스 로직은 아래와 같이 수정함
public class StatefulService {
public int order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
return price;
}
}
컴포넌트 스캔
컴포넌트 스캔과 의존관계 자동주입
지금까지 스프링 빈을 등록할 때는 자바코드의 @Bean 이나 xml의 beans를 통해 설정 정보에 저장
스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔을 제공함
의존관계도 자동으로 주입하는 @Autowired 도 지원한다.
컴포넌트 스캔을 사용하려면 먼저 @ComponentScan 을 설정 정보에 붙여주면 된다.
컴포넌트 스캔 기본 대상
@Component 뿐만 아니라 다음과 같은 내용도 추가로 대상에 포함한다
@Component : 컴포넌트 스캔에서 사용 @Controller : 스프링 MVC 컨트롤러에서 사용 @Service : 스프링 비즈니스 로직에서 사용 @Repository : 스프링 데이터 접근 계층에서 사용 @Configuration : 스프링 설정 정보에서 사용
컴포넌트 스캔의 용도 뿐만 아니라 다음 어노테이션 존재시 스프링은 부가 기능을 수행함
@Controller : 스프링 MVC 컨트롤러로 인식 @Repository : 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다. @Configuration : 앞서 보았듯이 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리 를 한다. @Service : 사실 @Service 는 특별한 처리를 하지 않는다. 대신 개발자들이 핵심 비즈니스 로직이 여기에 있겠구나 라고 비즈니스 계층을 인식하는데 도움이 된다.
'BackEnd > Java' 카테고리의 다른 글
[Java] 스프링 개구리 책 - 붕어빵틀과 붕어빵에 대한 오해 (0) | 2024.05.27 |
---|---|
[Java] 순수 자바 코드로 스프링 프레임워크 구현하기 - Servlet (0) | 2024.05.16 |
[Java] Optional(옵셔널) 이란? (0) | 2024.03.21 |
[Java] Thread의 내부 생성자, 메소드 (0) | 2023.12.08 |
[Java] 김영한의 실전 자바 정리 2 - 패키지 ~ 메모리 구조와 static (1) | 2023.12.04 |