[전문가를 위한 스프링 5] 3장_Spring IoC 와 DI

1. IoC 종류

IoC (Inversion of Control ) 은 두 가지로 나뉜다 : DI, DL
DI 는 IoC 컨테이너가 컴포넌트에 의존성을 주입시켜 준다. 반면, DL 은 컴포넌트 스스로 의존성 참조를 가져온다.

  • DI (Dependency Injection)

    • 생성자 주입 : IoC 컨테이너는 해당 컴포넌트를 초기화할 때, 컴포넌트에 필요한 의존성을 전달

      1
      2
      3
      public ConstructorInjection(Dependency dp){
      this.dp = dp;
      }
    • 세터 주입 : setter method 를 호출해서, 의존성을 나중에 제공 가능

      1
      2
      3
      public void setDependecy(Dependency dp){
      this.dp = dp;
      }
    • 필드 주입 : 스프링 컨테이너가 reflection 을 이용해 필요한 의존성을 주입

      1
      2
      3
      4
      5
      @Service
      public class Singer {
      @Autowired
      private Inspiration inspirationBean;
      ...
  • DL (Dependency Lookup)

    • Depenency Pull : 중앙 registry 에서 의존성을 직접 가져오는 방식 ( register -> container )

      1
      context.getbean();
    • Contextualized Dependency Lookup : 특정 중앙 registry 에서 의존성을 가져오는 것이 아니라, 자원을 관리하는 컨테이너에서 의존성을 가져오는 방식

      1
      2
      3
      publicv void lookup(Container ct){
      this.dp = (Dependency) ct.getDependency("myDependency");
      }

그렇다면, 의존성 주입 vs 의존성 룩업 ? 의존성 주입을 사용해라. 주입을 이용하면,

  • 사용자 클래스는 IoC 컨테이너와 완전리 분리된다.
  • 직접 테스트용 의존성을 주입하기 쉬우므로 테스트 하기 쉽니다.

그렇다면, 생성자 주입 vs 세터 주입 vs 필드 주입? 상황에 따라 선택해라.

  • 생성자 주입 : 컴포넌트에 의존성 주입을 보장해야할 때
  • 세터 주입 : 새로운 객체를 생성하지 않고 의존성을 교체할 때
  • 필드 주입 : 다음 이유로, 권장하지 않는다.
    • 클래스가 비대해지는 상황을 생성자 주입이나 수정자 주입에서는 쉽게 알아 챌 수 있지만, 필드 주입은 아니다.
    • 클래스는 public interface 의 메서드나 생성자로 필요한 의존성 타입을 명확히 전달해야하는데, 필드 주입을 이용하면 어떤 타입의 의존성이 필요한지 명확하지 않다.
    • final 필드에 사용할 수 없다.
    • 의존성을 수동으로 주입해야하므로, 테스트 코드 작성이 어렵다.

2. BeanFactory, ApplicationContext

  1. Bean : 컨테이너가 관리하는 모든 컴포넌트
  2. BeanFactory interface : 컴포넌트의 라이프사이클과 의존성을 관리
  3. ApplicationContext interface (extends BeanFactory) : DI 외에도, 트랜잭션, AOP, 애플리케이션 이벤트 처리 기능 제공

3. Application Context 구성

Read more

Bean

Bean 이 무엇이고, Spring Boot 는 어떤 과정으로 Bean 을 등록되는지 정리한다.

Bean 이란

IoC (Inversion of Control) Container 가 관리하는 객체이다.
IoC Container 는 객체의 생성을 책임지고 의존성을 관리한다. Spring 에서는 ApplicaitonContext 가 IoC Container 역할을 한다. ApplicaitonContext 는 BeanFactory 를 확장한다.

Bean 등록 순서

다음 순으로 Bean 이 등록된다.

  1. @ComponentScan
    최상위 패키지 이하의 모든 패키지에 있는 클래스에, @Component 가 붙어있으면 Bean 으로 등록한다.
    예를 들어, 다음과 같이 repository package 와 service package 에 각각 @Repository, @Service 가 붙은 클래스는 Bean 으로 등록된다. @Repository, @Service 도 결국 @Component 가 붙어있기 때문이다.
    ( Bean 등록 대상은, IDE 에서 클래스 옆에 콩 모양의 이미지를 보여준다. )

  2. @EnableAutoConfiguration
    spring-boot-autoconfigure 프로젝트의 META-INF 에 spring.factories 라는 파일이 있다.
    이 파일 안의 key 값 중에 EnableAutoConfiguratoin 이라는 키가 있는데, 이 키에 여러 Configuration 클래스가 정의되어있다.

    그리고 각 Configuration 클래스에는 @Configuration 이 붙어있다.
    예를 들어, WebMvcAutoConfigurration 를 따라가보면 결국에는 @Component 가 붙어 있기 때문에 Bean 으로 등록된다.

Read more

Spring MVC 요청 처리 과정

Spring MVC 는 어떻게 Client 의 요청을 처리하는지에 대해 정리한다.

DispatcherServlet

DispatchServlet 은 Client 의 요청에 대해 실제 처리하는 메서드를 호출해주는 Front Controller 역할을 하는 클래스이다.
계층 구조는 다음과 같다.

이제, DispatchServlet 이 Client 의 요청을 받은 뒤에, 어떻게 요청을 처리할 Controller 를 찾고 응답하는지 알아보자.

Client 요청 처리 흐름

전체 흐름은 다음과 같다.

  1. Application 이 구동될 때, Spring Boot Auto Configure 로 Spring MVC 의 Bean 들이 자동 등록된다.
  2. 사용자의 요청이 들어오면, DispatcherServlet 의 doService() 가 실행된다.
  3. doService() 에서 여러 설정을 한 뒤, doDispatch() 가 실행이 된다.
  4. HandlerMapping 이 요청을 처리할 Handler 를 찾는다.
  5. 등록되어 있는 HandlerAdapter 중에 해당 Handler 를 실행할 Adapter 를 찾는다.
  6. HandlerAdapter 는 Handler 의 응답을 처리한다.
  7. 최종적으로 응답을 보낸다.

디버거를 이용해서, 위 흐름을 코드로 상세히 살펴보자.

  1. Client 가 GET 요청을 하면, FrameworkServlet 의 doGet() 이 실행된다.

  2. processRequest() 의 doService() 에 들어가자.

Read more