□ DI (Dependency Injection)
■ 정의
- 스프링 컨테이너가 지원하는 핵심 개념 중 하나.
- 객체 사이의 의존 관계를 객체 자신이 아닌 외부의 조립기가 수행.
■ 예제
○ WriteArticleServiceImpl과 ArticleDao의 관계
- WriteActicleServiceImpl 클래스는 ArticleDao 인터페이스에 의존.
(실제로는 ArticleDao 인터페이스를 구현한 MysqlArticleDao 클래스의 객체나 OracleArticleDao 클래스의 객체에 의존.)
○ 실제 생성된 객체 간의 의존 관계
- WriteActicleServiceImpl 클래스는 실제로 의존할 객체를 지정할 수 있는 방법을 필요로 함.
○ 실제로 의존하는 객체를 지정하는 방법
● 코드에 직접 명시
- WriteArticleServiceImpl 클래스 코드에 직접 MysqlArticleDao 객체에 의존한다고 명시.
public class WriteArticleServiceImpl {
// 코드에 직접 의존 객체 명시.
private ArticleDao articleDao = new MysqlArticleDao();
} |
□ 단점
- 단위 테스트를 어렵게 만듬.
(올바르게 동작하는 MysqlArticleDao 클래스가 반드시 존재해야 함. articleDao 멤버 필드에 mock 객체를 할당할 수 없음.)
- 의존하는 클래스가 변경되는 경우 코드를 변경해야 함.
(의존하는 클래스가 MysqlArticleDao에서 OracleArticleDao로 변경해야하는 경우 코드를 변경한 뒤 재컴파일 해야함.)
● Factory 패턴이나 JNDI 등을 사용해서 의존 클래스를 검색
public class WriteArticleServiceImpl {
private ArticleDao articleDao = ArticleDaoFactory.create();
} |
- ArticleDaoFactory 클래스는 ArticleDao 인터페이스를 구현한 클래스 중 에서 어떤 클래스를 사용해야 할지의 여부를 알기 위해
외부의 설정파일을 사용할 수 도 있고, 시스템 프로퍼티를 사용할 수 도 있음.
□ 단점
- 단위 테스트를 하려면 올바르게 동작하는 Factory 또는 JNDI에 등록된 객체를 필요로 함.
● 외부의 조립기를 사용
- 의존 관계에 있는 객체가 아닌 외부의 조립기가 각 객체 사이의 의존 관계를 설정.
- 조립기가 의존 관계를 관리해 주는 방식을 Dependency Injection (DI) 패턴이 라고 함.
□ DI 패턴에서는 조립기(assembler)가 객체 간의 의존 관계를 관리
- WriteArticleServiceImpl 클래스의 코드는 MysqlArticleDao 객체를 생성하거나 검색하기 위한 코드가 포함되어 있지 않음.
- 조립기의 역할을 하는 Assembler가 MysqlArticleDao 객체를 생성한 뒤 WriteArticleServiceImpl 객체에 전달.
□ WriteArticleServiceImpl class
public class WriteArticleServiceImpl {
private ArticleDao articleDao;
// 생성자에서 의존하는 객체를 전달 받음.
public WriteArticleServiceImpl(ArticleDao articleDao) { this.articleDao = articleDao; }
} |
- 의존 객체를 직접 생성하지도 않고, Factory나 JNDI를 이용하지도 않음.
(WriteArticleServiceImpl 클래스는 단지 의존 객체를 전달 받을 수 있는 메서드나 생성자만을 제공.)
□ Assembler class
- WriteArticleServiceImpl 객체에 의존 객체를 전달.
public class Assembler {
public WriteArticleService getWriteArticleService() { ArticleDao articleDao = new MysqlArticleDao(); WriteArticleService service = WriteArticleServiceImpl(articleDao);
return service; }
} |
□ UsingService class
public class UsingService {
public void useService() {
// 조립기로부터 사용할 객체를 구함.
WriteArticleService service = assembler.getWriteArticleService();
service.write(...);
......
}
} |
- 조립기로부터 WriteArticleServiceImpl 객체를 구해서 사용.
□ DI 패턴 적용 시 장점
- 불필요한 의존 관계를 없애거나 줄일 수 있다.
- 인터페이스에만 의존.
- 단위 테스트를 수행하는게 수월해짐. (mock 객체와 같은 가짜 객체를 이용하여 클래스를 테스트할 수 있음.)
□ 스프링에서의 DI
■ 예제
○ DI 패턴에서는 조립기(assembler)가 객체 간의 의존 관계를 관리
○ DI 패턴을 적용한 자바 코드
- WriteArticleServiceImpl 클래스는 생성자나 설정 메서드를 이용하여 의존 객체를 전달 받을 수 있음.
● WriteArticleServiceImpl 클래스
/* 생성자 방식 이용. */
package kame.sping.chap01;
public class WriteArticleServiceImpl implements WriteArticleService {
private ArticleDao articleDao;
public WriteArticleServiceImpl(ArticleDao articleDao) {
this.articleDao = articleDao;
}
@Override
public void write(Article article) {
System.out.println("WriteArticleServiceImpl.write() 메서드 실행");
articleDao.insert(article);
}
} |
/* 설정 메서드 방식 이용. */
package kame.sping.chap01;
public class WriteArticleServiceImpl implements WriteArticleService {
...
public void setArticleDao(ArticleDao articleDao) {
this.articleDao = articleDao;
}
@Override
public void write(Article article) {
System.out.println("WriteArticleServiceImpl.write() 메서드 실행");
articleDao.insert(article);
}
} |
● MysqlArticleDao.java
package kame.sping.chap01;
public class MysqlArticleDao implements ArticleDao {
@Override
public void insert(Article article) {
System.out.println("MysqlArticleDao.insert() 실행");
}
} |
○ 스프링 설정 파일을 이용한 의존 관계 설정
- WriteArticleServiceImpl 객체와 MysqlArticleDao 객체 사이의 의존 관계를 처리.
● applicationContext.xml
- 스프링 설정 파일. (클래스 패스에 위치)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http: xmlns:xsi="http:
xsi:schemaLocation="http:>
<bean name="writeArticleService" class="kame.spring.chap01.WriteArticleServiceImpl">
<constructor-arg>
<ref bean="articleDao"/>
</constructor-arg>
</bean>
<bean name="articleDao" class="kame.spring.chap01.MysqlArticleDao">
</bean>
</beans> |
/* 코드로 표현한다면 */
MysqlArticleDao articleDao = new MysqlArticleDao();
WriteArticleServiceImpl writeArticleService = new WriteArticleServiceImpl(articleDao); |
□ <beans> 태그
- 스프링 설정 파일의 루트 태그.
□ <bean> 태그
- 스프링은 각 객체를 빈(bean)으로 관리.
- name 속성 : 빈의 이름, class 속성 : 생성될 객체의 클래스 타입.
□ <constructor-arg> 태그
- 생성자에 전달할 파라미터를 명시하기 위해 사용.
○ 스프링 컨테이너로부터 빈 객체 가져와 사용
- 스프링을 이용하여 설정 파일을 로딩한 뒤, 설정 파일에 명시한 빈 객체를 사용.
● Main.java
- 설정 파일로부터 BeanFactory를 생성하고 BeanFactory로부터 필요한 빈 객체를 가져와서 사용.
package kame.spring.chap01;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xm.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class Main {
public static void main(String[] args) {
Resource resource = new ClassPathResource("applicationContext.xml");
// Resource가 나타내는 XML 파일로부터 스프링 설정 내용을 로딩하여 빈 객체를 생성.
// 빈 객체를 관리하는 컨테이너.
BeanFactory beanFactory = new XmlBeanFactory(resource);
WriteArticleService articleService = (WriteArticleService)beanFactory.getBean("writeArticleService");
articleService.write(new Article());
}
} |
/* output */
WriteArticleServiceImpl.write() 메서드 실행
MysqlArticleDao.insert() 실행 |
- BeanFactory 객체를 생성하면, BeanFactory로부터 빈 객체를 가져와 사용 가능.
- BeanFactory.getBean(String name)을 사용하여 <bean>태그의 name 속성에 부여한 이름의 빈 객체를 구할 수 있다.
출저 : http://blog.naver.com/chocolleto/30085630331