다른 Web Framework 들과 구별되는 Spring의 가장 큰 특징은 뭘까?
바로 IOC와 DI 라고 단언할 수 있을 것이다. 이것들은 곧 사람들이 다른 Framework를 두고 Spring을 고르는 이유이다.

Spring Container

Spring에서는 OCP, DIP를 지키기 위해서 구현 객체의 관리를 클라이언트 코드에서 담당하지 않는다.
Spring Container에서 객체(Spring Bean)를 생성하고, DI를 진행한다.
따라서 개발자는 직접 객체를 등록하고 의존성을 주입해 줄 필요가 없다.

Spring에서는 BeanFactory가 Spring Container인데, 우리는 상속자인 ApplicationContext를 사용한다.
BeanFactory보다는 ApplicationContext가 제공하는 부가기능이 더 많기 때문이다.

IOC(Inversion Of Control)

IOC, 즉 제어의 역전이란 프로그램의 제어 흐름이 역전 된다는 의미이다.
개발자 입장에서는, 원래 구현 객체가 프로그램의 흐름을 스스로 제어하는게 당연하다.
하지만 Spring에서는 IOC 때문에 Spring Container 에게 제어권이 넘어간다.

DI(Dependency Injection)

DI, 즉 의존성 주입이란 IOC를 실현하기 위한 도구라고 할 수 있다.
내가 구현체 안에서 직접 어떤 구현 객체를 사용할 것인지를 고르는 것이 아닌,
실행 시점에 Spring Container 에서 내 구현체로 의존성을 주입해주는 것이다.

정적인 Class Dependency

정적인 클래스 의존관계는, 굳이 Application을 실행하지 않아도 확인할 수 있다.
Diagram을 통해서 어떤 클래스가 어디에 상속되어 있는지 한 눈에 확인이 가능하다.

동적인 Class Dependency

실행 시점에 주입되는 의존 관계들이기 때문에, App을 실행시켜 봐야 확인이 가능하다.
이는 Class Diagram이 아닌, 객체 Diagram을 실행 결과와 함께 확인해야 알 수 있다.

다양한 DI
  • 생성자 주입
    • 생성자 호출 시점에 단 한번만 주입이 된다.
    • 불변, 필수 의 의존 관계에서 사용한다.
  • 수정자(Setter) 주입
    • 어떤 Class의 Field 값을 수정할 때, 우리는 Setter Method를 사용한다.
    • 그것과 동일하게 Setter Method를 통해서 의존 관계를 주입하는 경우이다.
    • 선택, 변경 가능성이 있는 의존 관계에서 사용된다.
  • 필드 주입
    • 말 그대로 Class의 Field에 주입을 하는 방식이다.
    • 하지만 이 방식은, 외부에서 변경이 불가능해서 테스트가 힘들다.
      • DI Framework 내부가 아니면, 아무것도 할 수 없다.
    • 안쓰는게 좋음!
  • 일반 메서드 주입
    • 한번에 여러 필드를 주입받을 수 있다.
    • 하지만 거의 쓰지 않는다.

웬만하면 생성자 주입을 선택하자!

  • 불변
    • 대부분의 의존 관계는 한 번 주입되면 종료시점까지 변경될 일이 없다.
    • 수정자 주입은 public으로 setter을 만들어야 해서, 변경의 위험성이 있다.
  • 누락
    • Framework 없이, 순수 자바 코드 단위 테스트를 하는 경우에 유용하다.
      • 내가 생성자에 필요한 어떤 요소를 누락했는지 바로 알 수 있다.
    • 변수에 final 키워드를 사용하는 경우엔, 생성자에서 주입이 누락된 경우 애초에 컴파일이 안된다!
      • 오직 생성자 주입 방식만 final 키워드를 사용할 수 있다. (Java 문법)

Framework? Library?

  • Framework
    • 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크이다.
    • 프레임워크는 큰 뼈대이다.
  • Library
    • 내가 작성한 코드가 직접 흐름을 제어한다면, 그것은 라이브러리이다.
    • 라이브러리는 내가 뼈대 위에서 사용할 수 있는 각종 부품이다.

Spring Bean

Spring Bean 이란 Spring Container 내에서 자체 생성하는 객체이다.

Bean으로 등록된 객체는, AnnotationConfigApplicationContext를 통해서 불러올 수 있다.
getBean(name, type) 을 통해서 Bean에 할당된 객체를 불러와 사용할 수 있다.

getBean(type) 만으로도 Bean 조회가 가능하지만,
동일한 Type의 Bean이 둘 이상일 경우, NoUniqueBean 에러가 발생한다.
물론, Map<String, "Type"> 을 이용해서 해당 Type을 모두 조회할 수는 있다.

사진 및 자료 출처 : 김영한의 스프링 핵심 원리 기본편