3. 의존성(의존관계) 주입이란 ?
• 호출할 객체를 직접 선언하는게 아니라 런타임시 외부에서 주입해주는 것
• 이게 가능한 이유는 구체적인 객체가 아닌 인터페이스에 의존하기 때문에
• 따라서, 객체 간의 의존관계를 정적인 클래스 관계에서는 알 수 없다.
4. 의존성(의존관계) 주입이 필요한 이유
• DIP, SRP, OCP를 지킨 좋은 설계를 가능하게 해준다.
✓ 구체적인 객체에 의존하고 있다 (DIP 위반)
✓ 따라서 다른 구현체로 변경해야 하는 경우
해당 코드를 변경해야 한다 (OCP 위반)
✓ 해당 클래스의 책임은 비즈니스 로직을
수행하는 것인데, 구현체까지 결정하고 있다
(SRP 위반)
✓ 인터페이스에만 의존한다 (DIP)
✓ 따라서 다른 구현체로 변경해야 하는 경우
해당 코드에 변경사항은 없다 (OCP)
✓ 해당 클래스는 비즈니스 로직만 수행한다
(SRP)
5. DI Container ??
• 자바 애플리케이션에서 사용하는 객체들을 생성하고 객체들 간의 의존관계 주입 등을 담당
하기 위해 스프링에서 제공하는 클래스
7. 카테고리
• STEP1. 컨테이너에서 AppCon
fi
g 클래스 활용하여 빈 등록하기
• STEP2. AppCon
fi
g를 통해 주입되는 객체는 싱글톤 객체여야 한다
• STEP3. 만들어진 컨테이너를 기반으로 초간단 애플리케이션 작성
8. STEP1. 컨테이너에서 AppConfig 클래스 활용하여 빈 등록하기
• 빈으로 등록하기 위해 AppCon
fi
g에 선언한 다양한 메서드의 메서드 이름, 리턴타입,
실제 구현체를 어떻게 가져오지 ?
✓ re
fl
ection, 제네릭 활용!
• 빈을 담아두는 자료구조는 뭐가 좋을까 ?
✓ 빈 이름/클래스 타입 or 클래스 타입 or 빈 이름으로 원하는 객체를 가져올 수 있어야 한다.
✓ 2개의 Map을 활용해서 (빈 이름 - 인스턴스) 쌍과 (타입 - 빈 이름 리스트) 쌍 만든다.
✓ 두 번째 Map에서 value가 리스트인 이유는 타입이 같은 빈이 여러 개인 경우도 있기 때문에
✓ 결과적으로 타입으로만 조회하는 경우 해당 타입의 빈 이름을 가져와서 그 이름으로 첫 번째 Map에서
인스턴스를 얻어올 수 있다.
• 컨테이너 클래스의 관계는 어떤식으로 구성 하는게 좋을까 ?
✓ 현재는 빈을 등록하고 조회하는 기능 위주의 컨테이너이지만 다른 기능의 확장성을 고려해서
Container 인터페이스에 기능별로 인터페이스를 상속한다(현재는 빈 관련 인터페이스만 상속)
✓ 실제 스프링의 ApplicationContext도 아래와 같이 되어있었다.
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver
9. STEP2. AppConfig를 통해 주입되는 객체는 싱글톤 객체여야 한다
• 수정자(setter)를 통한 의존관계 주입 방식
✓ public 으로 set메서드를 선언해야 하기 때문에 추후에 실수로라도 변경될 가능성이 있음
✓ 실수로 의존성 주입을 해주지 않으면 런타임시 NPE 발생할 수 있다.
• 생성자를 통한 의존관계 주입 방식
✓ 인스턴스 변수를
fi
nal로 선언할 수 있기 때문에 객체가 주입되고 나면 불변이다.
✓ 대부분의 의존관계는 애플리케이션 종료 전까지 불변해야하므로 불변이 보장되어야 한다.
✓
fi
nal로 선언한 변수에 대해 생성자에서 초기화를 하지 않으면 컴파일 오류가 나기 때문에
실수할 일이 없다.
✓ 스프링 공식 문서에서도 생성자를 통한 주입방식 권장
(https://docs.spring.io/spring-framework/docs/5.0.3.RELEASE/spring-framework-reference/core.html#beans-setter-injection)
✓ 따라서 생성자를 통한 의존관계 주입방식으로 결정
10. STEP2. AppConfig를 통해 주입되는 객체는 싱글톤 객체여야 한다
• 싱글톤 객체를 보장하는 방법 : 모든 구현체 클래스에 싱글톤 패턴 적용
✓ boilerplate 코드가 만들어진다.
✓ 싱글톤 패턴을 적용하면 유연성이 떨어진다(상속 불가 등)
✓ 싱글턴으로 생성되는 객체는 구현체를 내부에 선언하므로 객체간에 결합도가 높아진다.
• 싱글톤 객체를 보장하는 방법 : 템플릿 메서드 패턴 적용 ?
✓ AppCon
fi
g 클래스에 정의된 메서드 이름이 곧 빈 이름이 될 수 있기 때문에 공통된 추상메서드를
갖는 것은 맞지 않다고 생각
<템플릿 메서드 패턴 예시>
11. STEP2. AppConfig를 통해 주입되는 객체는 싱글톤 객체여야 한다
• 싱글톤 객체를 보장하는 방법 : Con
fi
g 클래스들에 공통적으로 적용될 수 있는 부모 클래스 생성
✓ Container가 여러번 생성되더라도 Con
fi
g에 있는 빈은 한 번만 등록될 수 있도록 static 선언
✓ 멀티스레딩 환경에서의 동시 접근 문제를 방지하기 위해 ConcurrentHashMap 사용
12. STEP3. 만들어진 컨테이너를 기반으로 초간단 애플리케이션 작성
CustomerService
(interface)
CustomerRepository
(interface)
PlannerService
(interface)
MyApp
- 회원가입
- 플래너에게 드레스 투어 요청
① 회원등록
② 드레스 투어 요청
③ 해당 회원 스케줄에
‘드레스 투어’ 추가
MyContainer
CustomerService
Impl
생성
PlannerService
Impl
TemporaryCustomer
Repository
생성 생성
13. 느낀점
• 내부가 어떻게 돌아가는지 꾸준히 관심을 갖자
• 프레임워크에 녹아있는 원리를 공부하자
• 자바 기초가 중요한 것 같다
• 테스트 코드 작성하자