티스토리 뷰


1. 스프링 IoC 컨테이너와 빈

  • Inversion of Control

    • 의존 관계 주입(Dependency Injection)이라고도 하며, 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아 사용하는 방법을 말함.

  • 스프링 IoC 컨테이너

    • BeanFactory

      • 스프링 프레임워크의 중요한 인터페이스

    • 애플리케이션 컴포넌트의 중앙 저장소

    • 빈 설정 소스로부터 빈 정의를 읽어들이고, 빈을 구성하고 제공한다.

  • Bean - 빈

    • 스프링 IoC 컨테이너가 관리 하는 객체

    • 장점

      • 의존성 관리

        • 의존성 관리를 할 수 없다고 생각해보자

        • 예를 들어, 서비스 객체 내에서 Repository 의존성을 직접 만들어서 사용하는 경우, 서비스 단의 단위 테스트를 독립적으로 작성하기가 힘들다.

        • 의존성을 주입 받을 수 있기 때문에 객체(Repository)를 Mocking 해서 얼마든지 단위 테스트를 할 수 있다.

      • 스코프

        • 싱글톤: 하나

          • 빈의 기본 스코프 설정은 싱글톤

          • 메모리 관리 등 성능 최적화 이점

        • 프로토타입: 매번 다른 객체 생성

      • 라이프 사이클 인터페이스

        • @PostConstruct 등

  • ApplicationContext

    • ApplicationContext는 spring-context 모듈 안에 위치함

    • spring-beans 모듈의 BeanFactory(스프링 IoC 컨테이너)를 상속받고, 그외 다양한 기능을 가지는 인터페이스도 추가로 상속받은 인터페이스

      • ListableBeanFactory

      • HierarchicalBeanFactory

      • EnvironmentCapable

        • spring-core 모듈

        • 프로파일과 프로퍼티를 다루는 인터페이스

      • MessageSource

        • spring-context 모듈

        • 메세지 소스 처리 기능(i18n)

      • ApplicationEventPublisher

        • spring-context 모듈

        • 이벤트 발행 기능

      • ResourcePatternResolver

        • spring-core 모듈

        • ResourceLoader

        • 리소스 로딩 기능 등

2. ApplicationContext와 다양한 빈 설정 방법

  • 스프링 IoC 컨테이너의 역할

    • 빈 인스턴스 생성

    • 의존 관계 설정

    • 빈 제공

  • ClassPathXmlApplicationContext로 빈과 의존관계 등을 XML 파일로 일일히 설정하는 방법이 있었고, 이 굉장한 번거로움을 피하고자 등장한 것이 component-scan 이다.

    • 특정 패키지 하위에 있는 빈이 될 클래스들을 모두 스캔해서 빈으로 등록하는 방법이다.

    • 빈 스캐닝시 기본적으로 @Component 어노테이션을 사용해서 빈으로 등록할 수 있고,

    • @Component를 확장한 @Controller, @Service, @Repository 모두 컴포넌트 스캔의 대상이 된다.

  • Java Config로 직접 빈 설정도 사용할 수 있다.

    • AnnotationConfigApplicationContext

      @Configuration
      public class ApplicationConfig{
         
         @Bean
         public BookRepository bookRepository() {
             return new BookRepository();
        }
         
         @Bean
         public BookService bookService() {
             BookService bookService() = new BookService();
             bookService.setBookRepository(boookRepository());
             return bookService
        }
      }
  • 사실, Java Config로 일일히 하는 것도 번거롭다. 그래서 여기서도 @ComponentScan을 사용할 수 있다 :)

    • ComponentScan 대상 지정을 basePackages와 basePackageClasses 두가지 방법으로 할 수 있지만, 더 타입세이프 한 방법은 클래스 기준으로 가져가는 것이다.

    • 이 방법이 Spring Boot를 기반으로 Spring을 사용하는 것과 가장 가까운 방법이다.

      @Configuration
      @ComponentScan(basePackageClasses = DemoApplication.class)
      public class ApplicationConfig {

      }
      public class DemoApplication {
         public static void main(String[] args) {
             ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
             
            ...
                 
        }
      }
    • 물론 스프링 부트에서는 ApplicationContext를 만드는 것 조차 @SpringBootApplication이 해준다.

      • @SpringBootApplication을 타고 들어가보면, @SpringBootConfiguration이 @Configuration을 가지고 있고, @ComponentScan이 선언되어있다.

      • 따라서 Java Config 설정때 만들었던 ApplicationConfig도 필요 없다.

      • @SpringBootApplication이 다 해준다.

      @SpringBootApplication
      public class DemoApplication {
         public static void main(String[] args) {
                 
        }
      }

3. Bean Scope

  • ApplicationContext에 생성된 빈들은 스코프를 가지고 있다. 아무 설정도 해주지 않았으면. default인 싱글톤 스코프를 가진다.

  • 스코프

    • 싱글톤

      • 애플리케이션 전반에 걸쳐서, 해당 빈의 인스턴스가 오직 한개

    • 프로토타입

      • 빈을 주입 받아올 때마다 새로운 빈이 생성된다.

  • 프로토타입 빈이 싱글톤 빈을 참조하면?

    • 아무 문제 없다.

  • 싱글톤 빈이 프로토타입 빈을 참조하면?

    • 프로토타입 빈이 업데이트가 안되네...???

      • 싱글톤 빈을 통해서 프로토타입 빈을 조회하면 동일한 빈이 참조 된단 이야기.

    • 업데이트 하려면, 여러가지 방법이 있지만 그중 하나. scoped-proxy를 쓰는 방법

      • scoped-proxy

        • 빈 선언부에 스코프 설정과 같이 함

          @Bean
          @Scope(value = "prototype"proxyMode = ScopedProxyMode.TARGET_CLASS)
          public class Proto {
           
          }
        • proxyMode를 설정해야함. default는 프록시를 사용하지 않는다는 옵션.

        • 빈에 적용해야하므로 ScopedProxyMode.TARGET_CLASS를 프록시 모드로 설정한다.

        • 이 설정은, 클래스기반의 프록시로 감싸서 빈으로 등록하고, 다른 빈들이 사용할 때 프록시로 감싼 빈을 사용하게 하라는 설정이다.

        • 이렇게 하지 않으면, 프로토타입 빈을 바꿔줄 여지가 없다.

        • 싱글톤 빈에서 프로토타입 빈을 사용하려면 이와 같은 설정을 하고, 싱글톤 빈이 프록시로 감싸진 빈을 참조하게 해서 매번 다른 빈을 참조하게 해야한다.

      • Object-Provider

        • 소스 코드에 침투함.

      • Provider(표준)

  • 싱글톤 객체 사용시 주의할 점

    • 필드가 공유된다.

    • 멀티스레드 환경에서 빈의 필드를 변경하려고 하기때문에 스레드 세이프한 코딩이 필수적이다.

    • 모든 싱글톤 빈들은 ApplicationContext 초기 구동시 인스턴스가 생성된다.

Reference







댓글