비록 시작은 코딩일기지만, 그 끝은 창대하게
어엿한 개발자 블로그로 성장할 수 있도록.
본 게시글은
Endless Creation Spring Study
에 사용하는 자료입니다.
조립
(객체 생성과 의존관계 설정)은 누군가가 해주고 나는 실행하는 로직만 수행사용 영역
과, 객체를 생성하고 구성(Configuration)하는 구성 영역
으로 분리@Configuration
public class AppConfig
{
@Bean
public MemberRepository memberRepository()
{
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy()
{
return new RateDiscountPolicy();
}
@Bean
public MemberService memberService()
{
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService()
{
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
}
여기서 @Configuration
, @Bean
Annotation을 지우면
순수 자바 코드로 작성된 DI 컨테이너 이다.
교재에서 나온 Assembler.class
와 같음 (오브젝트 팩토리
라고 불리기도 함)
다음과 같이 ApplicationContext
에 우리가 작성한 Config.class
를 넘겨주면
Spring
이 우리가 설정한 정보를 참고해서 구성하는 역할을 맡게된다.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Assembler.class);
우리가 직접 구성하지 않고 Spring
에 맡기는 이유 / 장점은 여러가지 있지만
Singleton
이 대표적이다.
Singleton Pattern
은 Instance
가 오직 1개만 생성되야 하는 경우에 사용되는 패턴이다.
예를 들어 레지스트리 같은 설정 파일의 경우 객체가 여러개 생성되면 설정 값이 변경될 위험이 생길 수 있다.
Instance 가 1개만 생성되는 특징을 가진 Singleton Pattern
을 이용하면, 하나의 Instance
를 메모리에 등록해서 여러 쓰레드가 동시에 해당 Instance
를 공유하여 사용하게끔 할 수 있으므로, 요청이 많은 곳에서 사용하면 효율을 높일 수 있다.
단, Singleton을 적용할때 Concurrency Problem
을 고려해야 한다.
Singleton 적용 전
Singleton 적용 후
Java의 Singleton Pattern 에는 다음과 같은 한계점이 있다.
그럼 Java
의 Singleton Pattern과 Spring
의 Singleton의 차이는?
Spring Container
는 Singleton Pattern
의 문제점을 해결하면서, 객체 인스턴스를 싱글톤(1개만 생성)으로 관리한다.
Java의 Singleton은 Class Loader에 의해 구현되고,
Spring의 Singleton은 Spring Container
에 의해 구현된다.
Java의 Singleton Scope는 코드 전체이고,
Spring의 Singleton Scope는 해당 컨테이너 내부이다.
Java로 구현하는 Singleton은 개발자의 로직에 따라 Thread safety를 보장할 수도 않을 수도 있고,
Spring에 의해 구현되는 Singleton은 Thread safety를 자동으로 보장한다.
여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 Singleton 객체는 Stateful
하게 설계하면 안되고 Stateless
하게 설계해야 한다.
Stateless
(무상태)
public class StatefulService
{
private int price; //상태를 유지하는 필드. 있으면 안된다.
public void order(String name, int price)
{
System.out.println("name = " + name + " price = " + price);
this.price = price; //여기서 문제. price가 변경될 수 있다.
}
public int getPrice()
{
return price;
}
}
나는 분명 10000원을 주문했는데 누군가 30000원을 주문했을 때
내 주문금액이 30000원으로 변경될 수 있다.
(교재 p91 참고)
@Configuration
는 바이트코드를 조작한다.
Spring Container
는 Singleton
Container
이다.
따라서 Spring Bean
이 Singleton이 되도록 보장해야 한다.
그런데 Spring
이 Java code
까지는 못건드리기 때문에 바이트코드를 조작하는 라이브러리를 사용한다(CGLIB
).
Config Spring 빈을 getBean()
메소드를 통해 조회하면 class Config
이 아니라
class Config$$EnhancerBySpringCGLIB$$bcd12d91
이런식으로 뒤에 뭐가 더 붙어 나온다.
Spring
이 CGLIB
라는 바이트코드 조작 라이브러리를 사용해서 Config 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
즉, @Configuration
을 붙이면 바이트코드를 조작하는 CGLIB
기술을 사용해서 Singleton
을 보장한다.
@Configuration
없이 @Bean
으로 빈 등록은 가능하지만 바이트조작이 되지 않아 Singleton 보장이 깨진다.