종속성 주입(DI)은 개체가 생성자 인수, 팩터리 메서드에 대한 인수 또는 개체 인스턴스가 생성된 후 개체 인스턴스에 설정된 속성을 통해서만 종속성(즉, 함께 작동하는 다른 개체)을 정의하는 프로세스입니다. (디자인 패턴)
Dependency Injection (DI) is a design pattern that allows you to remove the hard-coded dependencies between objects in your application, and instead, provide them with their dependencies through a central location.
- 팩토리 메서드
- 구체적으로 사용할 오브젝트를 결정해주는 메서드 (추상적인 개념)
- 종속성
- 의존 관계 (Dependency Relationship) 관점에서 바라볼 수 있다.
구체적인 클래스에 대한 의존관계를 가지고 있는 경우 의존 관계의 정도가 강하다. 즉 결합도가 강한데 이는 소프트웨어 공학 관점에서 올바르지 않다. 그렇기 때문에 의존 관계에 대한 결합도를 낮추는 것이 중요하다.
- 의존 관계의 결합도를 낮추는 방법으로는 구체적인 클래스와의 의존 관계를 형성하는 것이 아닌 인터페이스와 의존 관계를 형성하도록 하는 것이다.
The object does not look up its dependencies and does not know the location or class of the dependencies.
@Service
class MemoService(
private val memoRepository: MemoRepository, // Interface
private val matchRepository: MatchRepository // Interface
)
런타임 시점에 형성되는 의존 관계는 무엇인가? (조건 2가지 ?)
프로그램이 실행되기 전까지 구체적으로 의존 관계가 형성되는 오브젝트를 알 수 없다는 의미인데 기본적으로 런타임 의존관계는 인터페이스를 주입 받는 경우를 말한다.
또한 생성자나 팩토리 메서드를 통해 주입을 받아야 한다.
컨테이너나 팩토리등 제 3의 존재가 오브젝트 사이의 런타임 의존 관계를 결정하고 오브젝트를 주입해주는 것, 종속성을 결정하는 것
스프링에서 DI
이유
스프링 프레임워크에서 빈간의 주입을 해주는 것은 컨테이너이다. 컨테이너에서 관리되는 오브젝트여야지만 주입이 가능하다.
class SimpleMovieLister(private val movieFinder: MovieFinder) {}
class SimpleMovieLister {
lateinit var movieFinder: MovieFinder
}
Singleton 범위에 사전 인스턴스화가 설정된 빈은 컨테이너가 생성되면서 빈이 생성되고 주입 , 그렇지 않으면 요청될 때 빈이 생성
순환참조 문제란 A 클래스가 B 클래스의 Bean 을 주입받고, B 클래스가 A 클래스의 Bean 을 주입받는 상황처럼 서로 순환되어 참조할 경우 발생하는 문제를 의미
- 생성자 주입의 방식을 사용할 경우 어떠한 Bean도 생성하지 못하게 되어 무한 반복
- 필드 주입이나 수정자 주입의 경우 해당 오브젝트에 대한 메소드가 호출되어야 순환 호출의 문제가 발생
- 애초에 설계 시점에 순환 참조가 일어나지 않도록 하는 것이 중요하다.
컨테이너는 싱글톤 빈 A를 한 번만 생성하므로 속성을 설정할 수 있는 기회는 한 번뿐입니다. 컨테이너는 필요할 때마다 Bean B의 새 인스턴스를 Bean A에 제공할 수 없습니다.
해결 방법
해결책은 제어의 역전을 포기하는 것입니다. ApplicationContextAware 인터페이스를 구현하고 컨테이너에 대한 getBean("B") 호출을 수행하여 Bean A가 필요할 때마다 (일반적으로 새로운) Bean B 인스턴스를 요청함으로써 Bean A가 컨테이너를 인식하도록 할 수 있습니다.
다른 방법으로는 @Lookup 어노테이션을 활용하는 것
Annotation injection is performed before XML injection.
- Annotation 기반 주입이 먼저 일어나고 XML 주입이 일어난다 !
Spring에서 의존성 주입을 위해 지원하는 어노테이션
BeanPostProcessor의 구현체인 AutowiredAnnotationBeanPostProcessor가 빈의 초기화 라이프 사이클 이전, 즉 빈이 생성되기 전에 @Autowired가 붙어있으면 해당하는 빈을 찾아서 주입해주는 작업을 하는 것
찾는 순서
타입 -> 이름 -> @Qualifier -> 실패
생성자
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao)
수정자
class SimpleMovieLister {
@set:Autowired
lateinit var movieFinder: MovieFinder
}
@Order
또는 @Priority
사용 가능class MovieRecommender {
@Autowired
@Qualifier("main")
private lateinit var movieCatalog: MovieCatalog
}
Spring은 필드
또는 빈 주입 수정자 메소드
에 @Resource 어노테이션을 사용하여 주입을 지원한다. 즉 생성자에는 적용할 수 없다.
Java에서 지원하는 어노테이션
찾는 순서
이름 -> 타입 -> @Qualifier -> 실패
class SimpleMovieLister {
@Resource(name="myMovieFinder")
private lateinit var movieFinder:MovieFinder
}
@Component
class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)