MemberService에서 MemberRepository를 주입받고 내부에서 MemberRepository의 구현체인 MemoryMemberRepository로 객체를 초기화하게 되면 MemoryMemberRepository에 의존성이 생긴다.
MemberService에서 MemberRepository 인터페이스만 의존하게 하고, MemberService의 생성자만 만들어준다.
MemberService는 생성자를 통해 구현 객체를 주입받고 어떤 객체가 주입될 지는 외부(AppConfig)에서 결정된다. 의존 관계에 대한 고민은 외부에 맡기고 실행에만 집중한다.
AppConfig가 구체 클래스를 선택하는 역할을 가지고 나머지는 담당하는 기능을 실행하는데에만 집중할 수 있게 된다.
SRP 단일 책임 원칙(single responsibility principle)
하나의 책임만 가져야 한다
DIP 의존 관계 역전 원칙(dependency inversion principle)
구체화에 의존하지 않고 추상화에 의존한다
OCP 개방 폐쇄 원칙(Open Closed Principle)
확장에는 열려있으나 변경에는 닫혀있어야 한다.
기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 객체를 생성하고 연결하고 실행했다.
→ 구현 객체가 스스로 프로그램의 제어 흐름을 조종했다.
AppConfig가 등장한 이후에는 구현 객체는 '실행'의 역할만 갖게 되고 AppConfig가 프로그램의 제어 흐름을 가져간다. 구현 객체들은 어떤 구현체를 가지고 실행되는 지 알 수 없게 되고 실행의 역할만 수행하게 된다.
→ 프로그램의 제어 흐름을 직접 제어하는 게 아니라 외부에서 관리하게 되는 것을 제어의 역전이라고 한다.
프레임워크 V. 라이브러리
정적인 클래스 의존 관계
import 코드만 보고 클래스간의 의존관계를 파악할 수 있고 애플리케이션을 실행하지 않아도 분석할 수 있다.
아래 다이어그램에서 실행 코드는 실선만 가져온다. OrderService의 구현체인 OrderServiceImpl은 MemberRepository와 DiscountPolicy에 의존하지만 실제 구현체로 어떤 객체가 주입될 지 알 수 없다.

동적인 객체 인스턴스 의존 관계
어떤 객체가 어디에 주입될 지는 애플리케이션 런타임에 외부(여기서는 AppConfig)에서 구현 객체를 생성하고 연결해서 의존관계가 연결된다. 이것을 의존관계 주입이라고 한다.
의존관계 주입을 사용해 실행 코드를 변경하지 않고 호출하는 대상의 타입 인스턴스를 변경할 수 있다.
정적인 클래스 의존관계를 변경하지 않고 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.

IoC 컨테이너, DI 컨테이너
@Configuration 어노테이션을, AppConfig 클래스 안에서 객체 주입을 해주는 메서드에 @Bean 어노테이션을 붙인다.@Configuration이 붙은 구성 정보가 들어있는 클래스를 이용해 @Bean 어노테이션이 붙은 메서드를 모두 호출해 반환된 객체를 빈으로 등록하고 관리한다. 메인 메서드에서 AnnotationConfigApplicationContext를 이용해 applicationContext 변수를 만들고 getBean() 메서드를 이용해 객체를 꺼낸다. 책 스프링 입문을 위한 자바 객체지향의 원리와 이해(개구리책)에서는 ApplicationContext를 우리가 쓸 객체를 모아놓은 종합 쇼핑몰에, Bean을 등록하는 행위를 판매 상품을 진열에, getBean()을 해당 객체를 구매하는 행위에 비유한다.getBean() 시에는 인자로 빈의 이름과 반환할 객체의 클래스(?)를 넘겨준다.public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = ac.getBean("memberService", MemberService.class);
memberService.
}