SPRING - 오브젝트와 의존관계 (6)

🚀 싱글톤 레지스트리와 오브젝트 스코프

✏️ 오브젝트의 동일성과 동등성

자바에서는 두 개의 오브젝트가 완전히 같은 동일한 오브젝트라 말하는 것과, 동일한 정보를 담고있는 오브젝트라고 말하는 것은 분명한 차이가 있습니다.
전자는 동일성비교라고 하고, 후자를 동등성 비교라고 합니다.

  • 동일성
    • 두 개의 오브젝트가 동일하다면 사실은 하나의 오브젝트만 존재하는 것이고 두 개의 오브젝트 레퍼런스 변수를 갖고 있을 뿐
  • 동등성
    • 두 개의 각기 다른 오브젝트가 메모리상에 존재하는 것, 변수 명이 같다거나 등등 적절한 동등성 기준을 두고 판단

스프링 애플리케이션 컨텍스트와 순수 자바 코드로 만든 오브젝트 팩토리의 동일성을 판별해봅시다.

오브젝트 팩토리

@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. 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 필드를 정의
    3. 한 번 오브젝트가 만들어지고 난 뒤에는 메소드를 통하여 이미 만들어져 스태틱 필드에 저장해둔 오브젝트를 넘겨줌

싱글톤 패턴의 한계
1. private 생성자를 갖고 있기 때문에 상속 불가.
2. 테스트하기가 어려워짐

  • 싱글톤은 초기화 과정에서 생성자등을 통해 사용할 오브젝트를 다이내믹하게 주입하기가 힘듬
  1. 서버환경에서는 싱들톤이 하나만 만들어지는 것을 보장하지 못함
    • 서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글톤 클래스임에도 하나 이상의 클래스가 만들어질 수 있음
  2. 싱글톤의 사용은 전역 상태를 만들 수 있음
    • 스태틱 필드이기 때문에 언제든지 접근이 용이해짐. 즉, 아무 객체나 자유롭게 접근하고 수정이 가능해집니다.

싱글톤 레지스트리

이런 한계에도 불가 하고 스프링은 싱글톤 사용을 적극 권장합니다. 한계를 극복하기 위해 스프링이 직접 싱글톤형태의 오브젝트를 만들고 관리하는 기능을 제공합니다.
그것을 바로 싱글톤 레지스트리라고 부릅니다.

싱글톤 레지스트리는 스태틱 필드와 private 생성자를 사용하지 않고 평범한 자바 클래스를 싱글톤으로 활용할 수 있게 해줍니다.
덕분에 싱글톤 방식으로 사용됭 애플리케이션 클래스라도 public 생성자를 가질 수 있습니다.

🔧 싱글톤과 오브젝트의 상태

싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근해서 사용할 수 있습니다. 따라서 상태관리에 주의를 기울여야 합니다.
멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 합니다.

왜냐하면, 싱글톤으로 유지시 저장할 공간은 하나 뿐이니 원치않은 값을 읽을 수 도 있고, 덮어 쓸수도 있기 때문입니다.
물론, 읽기전용의 값이라면 초기화 시점에서 인스턴스 변수에 저장해두고 공유하는 것은 아무런 문제가 없습니다.

그렇다면, 각 요청에 대한 정보나, 리소스로부터 생성한 정보가 필요한 경우는?? 이떄는 파라미터, 로컬 변수, 리턴 값등을 사용하면 됩니다.

👋 다음 포스팅에서 이어서 하도록 하겠습니다!

좋은 웹페이지 즐겨찾기