본문 바로가기
학습/Spring

[spring] 핵심1_Inversion Of Control / Bean / Dependency Injection

by KKambi 2020. 3. 21.

Inversion Of Control (IOC)

  • 의존성 관리를 내 클래스 밖에서 누군가 알아서 해준다 -> 제어권의 역전
  • 클래스 안에서 필요한 인스턴스를 생성하지 않는다 -> 주입받음
  • Dependency Injection -> @Autowired / Constructor / Setter
  • 따라서, DI는 IOC를 구현하는 방법이라 할 수 있다.
  • 이 때, 의존성의 타입(또는 인터페이스)만 맞으면 어떤 것이든 상관없다.
  • 또한, 코드 테스트도 편해진다.

 

IOC Container

  • Bean 객체를 관리하는 컨테이너
  • 기능 = Bean생성 + Bean사이의 의존성 엮기 + Bean제공
  • ApplicationContext 또는 BeanFactory (주로 전자를 의미)
  • ApplicationContext는 BeanFactory를 상속받고 더 다양한 기능
  • 그러나, 직접 사용할 일은 거의 없음

 

직접 ApplicationContext를 사용하고 싶다면?

  • @Autowired를 통해 ApplicationContext 타입 인스턴스를 생성
  • 이 객체 안에는 모든 Bean객체가 포함되어 있다.
  • getBean( )으로 특정이름 / 특정타입의 Bean을 가져올 수 있다.

 


 

Bean 등록 방법 = 특정 인스턴스를 IOC Container에 넣는 방법

  1. Component Scan
  2. @Configuration 클래스 안에 직접 등록

 

방법1 - Component Scan

  설명
작동방식

- Bean의 생명주기 = Bean Lifecycle

- Bean의 특정 작업에 대한 Lifecycle Callback 존재

- Lifcycle Callback는 스프링 IOC컨테이너가 사용하는 인터페이스 존재

- 이런 인터페이스들 중, @Component가 붙은 클래스를 모두 찾아서 인스턴스를 만들고, Bean으로 등록하는 Annotation Processor 존재

이해1

- @SpringBootApplication을 선언하면, 이 위치에서부터 하위 패키지의 모든 클래스를 스캔한다.

- 왜냐하면 @SpringBootApplication 안의 @ComponentScan이 어디부터 컴포넌트를 찾아보라고 알려주기 때문

이해2

- @Controller / @Repository / @Service / @Configuration은 모두 메타 어노테이션 @Component를 사용하기 때문에 스캔되는 것

이해3

- 그렇지만 Repository는 보통 JPA 이용

- JPA는 Repository인터페이스를 상속하고 있는 인터페이스의 구현체를 만들고 Bean으로 등록해준다.

이해4

- 클래스에 직접 @Component를 붙이는 경우도 있다.

 

방법2 - 직접 등록

  설명
어떻게?

- java source code || xml

- 대세는 전자이므로, 이 방식만 설명

자세히

- @Configuration이 붙은 클래스 생성

- @Bean이 붙은 메소드 선언

- 해당 메소드에서 인스턴스를 생성하여 반환

이해

- @Component 스캐닝이 되지 않아도 Bean으로 등록

- 왜냐하면 @Configuration도 @Component를 사용하기 때문에 이를 스캔하는 과정에서 Annotation Processor가 @Bean을 보고 등록한다.

 

모든 객체가 Bean으로 등록되진 않는다

  • Bean이 아닌 객체는 기본적으로 주입하지 않는다.
  • ex) @Entity는 Bean으로 등록되지 않는다.

 

★ Bean = Singleton Scope Object

  • 특정 타입의 Bean = 컨테이너 안에 단 1개만 존재
  • 1개 인스턴스를 어플리케이션 전반에서 재사용

 

싱글톤 스코프 객체 확인

  • 생성자 인자로 받은 Bean객체의 Hash값
  • ApplicationContext.getBean(객체타입)으로 가져온 객체의 Hash값
  • 서로 동일함!

 

싱글톤 객체 in 멀티스레드

  • 사실 스프링의 싱글톤은 멀티스레드에서의 동기화를 보장하지 않는다 (Not Thread-Safe)
  • 그러나 스프링에서 관습적으로 사용하는 설계는 싱글톤 객체가 가변적인 상태를 가지고 있게끔 하지 않는다.
  • 예컨대 빈으로 등록되는 @Controller, @Service와 같은 클래스들이 상태 변수를 가지고 있게끔 만들지 않는다.
  • 따라서 빈의 동기화를 신경쓰지 않는 것이다.

 


 

Dependency Injection (DI)

  • IOC Container가 타입에 맞는 Bean을 꺼내서 알아서 넣어준다.
  • 의존성 관리 = 필요한 의존성을 주입
  • 스프링이 아니어도 DI라는 개념은 적용가능

 

@Autowired를 통해 의존성을 주입받기

  1. Constructor Injection
  2. Field Injection
  3. Setter Injection

 

레퍼런스 권장 DI = 생성자

  1. 스프링 4.3부터 생성자가 받는 인자가 Bean이라면, @Autowired를 생략할 수 있음
  2. 필수적으로 요구되는 객체 없이는 인스턴스를 만들지 못하도록 강제

 

Field Injection / Setter Injection의 원리

  1. 스프링 IOC컨테이너가 인스턴스를 생성하고 나서, Field할당 / Setter를 통해서 알맞은 Bean을 찾아 주입한다.
  2. 일단 인스턴스는 생성된다는 단점!
  3. Circular Dependency 해결 가능하나, 순환참조가 일어나지 않도록 의존성을 조율하는 게 더 좋다.

 

 

 

 

참고

인프런-예제로 배우는 스프링 입문 (백기선)

5~8강 (스프링 IOC, 스프링 IOC 컨테이너)를 들으며 정리한 내용입니다.

 

 

댓글