//의존관계 검색
public UserDao(){
DaoFactory daoFactory = new DaoFactory(); //daoFactory가 미리 정의한 이름
this.connectionMaker = daoFactory.connectionMaker();
}
//의존관계 주입
public UserDao(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker();
}
1.7.1 제어의 역전(IoC)과 의존관계 주입
- IoC 단어는 스프링 서블릿 컨테이너처럼 서버에서 동작하는 서비스 컨테이너라는 뜻인지, 단순희 IoC 개념이 적용된 템플릿 메소드 패턴을 이용해 만들어진 프레임워크인지, IoC 특징을 지닌 기술인지 해석하기 애매함
- 의존관계 주입(Dependency Injection)은 의도가 명확한 이름임
- 스프링 IoC 기능의 대표적인 동작원리는 주로 의존관계 주입이라고 불림
- IoC 컨테이너라 불리는 스프링은 의존관계 주입 컨테이너 또는 DI 컨테이너라 불림
- DI는 오브젝트 레퍼런스를 외부로부터 제공(주입)받아 이를 통해 여타 오브젝트와 동적 의존관계를 만드는 것이 핵심
1.7.2 의존관계
- 의존관계는 항상 방향성이 있음
- A가 B를 의존하고 있으면 아래와 같은 다이어그램으로 나타낼 수 있음
- A가 B에 의존하고 있지만, 반대로 B는 A에 의존하지 않는다.
17.3 UserDao의 의존관계
- UserDao는 ConnectionMaker 인터페이스에만 의존하고 있음
- 인터페이스 구현 클래스와는 느슨한 결합을 가짐
- 인터페이스를 통해 의존관계를 제한하면 인터페이스 구현 클래스 변경에 자유롭게 됨
- UserDao는 DConnectionMaker 클래스에 의존하지 않음
- UML 상에서 나타나는 코드간 의존관계 외에 런타임 시에 오브젝트 사이에서 만들어지는 의존관계가 있음. 이를 런타임 의존관계 또는 오브젝트 의존관계라 함
- 런타임 시 의존관계를 맺는 대상(오브젝트)을 의존 오브젝트(Dependent Object)라 함
💡 의존관계 주입은 런타임시에 오브젝트를 연결하는 것임
1.7.4 의존관계 주입 세 가지 조건
- 인터페이스에만 의존을 해야함
- 코드에서 런타임 시점의 의존관계가 드러나지 않음
- 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정함
- 스프링에서의 애플리케이션 컨텍스트, 빈 팩토리, IoC 컨테이너 등이 제3의 존재임
- 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어짐
1.7.5 UserDao의 의존관계 주입
- DaoFactory는 오브젝트의 생성, 초기화, 제공 등의 작업을 수행하는 컨테이너임
- 따라서, 의존관계 주입을 담당하는 컨테이너이기 때문에 DI 컨테이너라 할 수 있음
- DaoFactory라는 제3의 존제가 런타임 시 의존관계 설정
- 파라미터를 통해 레퍼런스(new DConnectionMaker) 제공
- DI 컨테이너에 의해 런타임 시 의존 오브젝트를 사용할 수 있도록 그 레퍼런스를 전달받는 과정이 마치 메소드(생성자)를 통해 DI 컨테이너가 UserDao에게 주입해 주는것과 같다고 하여 이를 의존관계 주입이라 함
- 코드 레벨의 의존관계
- 런타임 시의 의존관계
💡 DI는 자신이 사용할 오브젝트에 대한 선택과 생성 제어권을 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다는 점에서 IoC 개념과 잘 맞음
1.7.6 의존관계 검색과 주입
- 의존관계 검색(DL, Dependency Lookup)은 런타임 시 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트의 생성작업은 외부 컨테이너에게 IoC로 맡기지만, 이를 가져올 때 메소드나 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용함
- 애플리케이션 컨텍스트가 미리 정의해 놓은 이름을 전달해서 그 이름에 해당하는 오브젝트를 찾는 것이 의존관계 검색임
- 의존관계 검색은 의존관계 주입의 거의 모든 장점을 가지고 있음. 하지만, 코드 상으로 의존관계 주입이 더욱 단순 및 깔끔함
- 의존관계 검색을 사용할 때가 있음. 애플리케이션의 기동 시점에서 적어도 한 번은 의존관계 검색 방식을 사용해 오브젝트를 가져와야함. 스태틱 메소드인 main()에는 DI를 이용해 오브젝트를 주입받을 방법이 없기 때문.
- 서블릿은 스프링이 미리 만들어서 제공하기 때문에 직접 구현할 필요 없음
- 의존관계 검색의 오브젝트는 의존관계 주입과 달리 스프링의 빈일 필요없음
//의존관계 검색
public UserDao(){
DaoFactory daoFactory = new DaoFactory(); //daoFactory가 미리 정의한 이름
this.connectionMaker = daoFactory.connectionMaker();
}
//의존관계 주입
public UserDao(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker();
}
💡 DI를 원하는오브젝트는 먼저 자기 자신이 컨테이가 관리하는 빈이 돼야 함
1.7.7 의존관계 주입의 응용 및 기능 구현의 교환, 부가기능 추가
- 스프링이 제공하는 기능의 99%가 DI의 혜택을 이용하고 있음
- 의존관계 주입을 이용하면 부가기능을 쉽게 추가할 수 있음
- 의존하는 인터페이스 사이에 새로운 인터페이스 구현 클래스를 추가하면 부가기능을 쉽게 추가 할 수 있음
- DB의 연결 횟수를 카운트 하는 로직을 추가할 때 UserDao와 ConnectionMaker 사이에 ConnectionMaker을 구현하는 CountingConnectionMaker 클래스를 추가함
public class CountingConnectionMaker implements ConnectionMaker {
int cnt = 0;
private ConnectionMaker realConnectionMaker;
public CountingConnectionMaker(ConnectionMaker connectionMaker) {
realConnectionMaker = connectionMaker;
}
@Override
public Connection getConnection() throws ClassNotFoundException, SQLException {
this.cnt++;
return realConnectionMaker.getConnection();
}
public int getCnt() {
return cnt;
}
}
@Configuration
public class DaoFactory {
@Bean
public UserDao userDao(){
return new UserDao(connectionMaker());
}
@Bean
public ConnectionMaker connectionMaker() {
return new CountingConnectionMaker(realConnectionMaker());
}
@Bean
public DConnectionMaker realConnectionMaker() {
return new DConnectionMaker();
}
}
public class UserDaoTest {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
//UserDao dao = new DaoFactory().userDao();
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao = applicationContext.getBean("userDao", UserDao.class);
CountingConnectionMaker ccm = applicationContext.getBean("connectionMaker",
CountingConnectionMaker.class);
System.out.println(ccm.getCnt());
...
}
}
1.7.8 메소드를 이용한 의존관계 주입
- 수정자(setter) 메소드를 이용한 주입
- 한 번에 하나의 파라미터 밖에 없음
- 파라미터로 전달된 값을 인스턴로 변수로 저장
- DI 받을 오브젝트에 set을 추가하여 메소드를 명명 하는게 일반적 관례
@Configuration public class DaoFactory { @Bean public UserDao userDao() { UserDao userDao = new UserDao(); userDao.setConnectionMaker(connectionMaker()); return userDao; } }
- 일반 메소드를 이용한 주입
- 여러 개의 파라미터를 가짐
- 실수가 생길 수 있음
'토비의 스프링 정리' 카테고리의 다른 글
토비의 스프링 - 1.9 1장 정리 (0) | 2022.09.26 |
---|---|
토비의 스프링 - 1.8 XML을 이용한 설정 (0) | 2022.09.26 |
토비의 스프링 - 1.6 싱글톤 레지스트리와 오브젝트 스코프 (0) | 2022.09.26 |
토비의 스프링 - 1.5 스프링의 IoC (0) | 2022.09.26 |
토비의 스프링 - 1.4 제어의 역전(IoC) (0) | 2022.09.26 |