자바에서는 두 개의 오브젝트가 완전히 같은 동일한 오브젝트라 말하는 것과, 동일한 정보를 담고있는 오브젝트라고 말하는 것은 분명한 차이가 있습니다.
전자는 동일성비교라고 하고, 후자를 동등성 비교라고 합니다.
스프링 애플리케이션 컨텍스트와 순수 자바 코드로 만든 오브젝트 팩토리의 동일성을 판별해봅시다.
오브젝트 팩토리
@RequiredArgsConstructor
public class AppConfig {
private final EntityManager em;
public UserRepositoryV5 userRepository(){
return new UserRepositoryV5Impl(em);
}
public UserServiceV5 userService(){
return new UserServiceV5(userRepository());
}
}
애플리케이션 컨텍스트
@Configuration
public class SpringAppConfigV1 {
@Bean
@Primary
public LocalEntityManagerFactoryBean getEmf(){
LocalEntityManagerFactoryBean emf=new LocalEntityManagerFactoryBean();
emf.setPersistenceUnitName("hello");
return emf;
}
@Bean
@Primary
public EntityManager getEm(){
return getEmf().getObject().createEntityManager();
}
@Bean
public UserRepositoryV5 userRepository(){
return new UserRepositoryV5Impl(getEm());
}
@Bean
public UserServiceV5 userService(){
return new UserServiceV5(userRepository());
}
}
테스트 코드
@Transactional
public class SingletonTest {
@Autowired
EntityManager em;
@Test
@DisplayName("자바 코드로 생성한 팩토리 테스트")
void 자바_오브젝트_테스트(){
AppConfig appConfig=new AppConfig(em);
UserServiceV5 userService1=appConfig.userService();
UserServiceV5 userService2=appConfig.userService();
Assertions.assertThat(userService1).isNotEqualTo(userService2);
}
@Test
@DisplayName("스프링 빈 애플리케이션 컨텍스트 테스트")
void 스프링_빈_테스트(){
AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(SpringAppConfigV1.class);
UserServiceV5 userService1=ac.getBean("userService",UserServiceV5.class);
UserServiceV5 userService2=ac.getBean("userService",UserServiceV5.class);
Assertions.assertThat(userService1).isEqualTo(userService2);
}
}
결과확인
결과를 보시면 자바 코드로 생성한 팩토리 클래스는 매번 생성시 동일성을 충족하지 않았고,
스프링 빈 애플리케이션 컨텍스트는 동일성을 충족했다는 것을 알 수 있습니다.
먼저 공부했을때 애플리케이션은 IOC컨테이너라는 것을 알 수 있었습니다.
추가로 애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리입니다.
스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱들톤으로 만듭니다.
서버 애플리케이션과 싱글톤
근본적인 질문! 그렇다면 스프링은 왜 싱글톤으로 빈을 생성할까?
스프링이 처음 설계됐던 이유로부터 비롯됩니다. 스프링은 초당 엄청난 수의 요청을 처리할 수 있는 높은 성능이 요구되며, 하나의 요청을 처리하기 위해
다양한 기능을 담당하는 무수히 많은 오브젝트들이 계층형으로 이루어져 있습니다.
그런데 매번 클라이언트의 요청이 올 때마다 각 로직을 담당하는 오브젝트를 새로만들어서 사용한다면 엄청난 부하가 걸리게 되겠죠?!
그렇기에, 스프링은 싱글톤으로 빈을 생성하게 됩니다.
하지만, 싱글톤 패턴을 사용하기는 까다롭고 여러가지 문제가 있다고 합니다.
싱글톤 패턴의 한계
1. private 생성자를 갖고 있기 때문에 상속 불가.
2. 테스트하기가 어려워짐
싱글톤 레지스트리
이런 한계에도 불가 하고 스프링은 싱글톤 사용을 적극 권장합니다. 한계를 극복하기 위해 스프링이 직접 싱글톤형태의 오브젝트를 만들고 관리하는 기능을 제공합니다.
그것을 바로 싱글톤 레지스트리라고 부릅니다.
싱글톤 레지스트리는 스태틱 필드와 private 생성자를 사용하지 않고 평범한 자바 클래스를 싱글톤으로 활용할 수 있게 해줍니다.
덕분에 싱글톤 방식으로 사용됭 애플리케이션 클래스라도 public 생성자를 가질 수 있습니다.
싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근해서 사용할 수 있습니다. 따라서 상태관리에 주의를 기울여야 합니다.
멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 합니다.
왜냐하면, 싱글톤으로 유지시 저장할 공간은 하나 뿐이니 원치않은 값을 읽을 수 도 있고, 덮어 쓸수도 있기 때문입니다.
물론, 읽기전용의 값이라면 초기화 시점에서 인스턴스 변수에 저장해두고 공유하는 것은 아무런 문제가 없습니다.
그렇다면, 각 요청에 대한 정보나, 리소스로부터 생성한 정보가 필요한 경우는?? 이떄는 파라미터, 로컬 변수, 리턴 값등을 사용하면 됩니다.