DI

DI(Dependency Injection) 란?
  • 스프링 IoC 컨테이너 핵심 개념 중 하나
  • 다양한 프레임워크에 이미 적용되어 있는 기능
  • 객체 간의 의존 관계를 외부의 조립기가 관리
  • 불필요한 의존 관계를 없애거나 줄일 수 있음
  • 단위테스트 수행 수월
  • 설정파일과 애노테이션을 이용하여 객체 간의 의존 관계를 설정
  • 각 객체를 빈(bean)으로 관리
1. 기본 어플리케이션 의존 관계 - 코드에 직접 의존 클래스를 생성하는 문제와 수정해야하는 경우 코드를 수정해야 하는 범위가 넓어진다.

public class Hello {
    public static void main(String[] args) {
        MessageBean bean = new MessageBean();
        bean.say("Spring");
    } // main
} // Hello

public class MessageBean {
    public void say(String name) {
        System.out.println("Hello, " + name + " !");
    } // say
} // MessageBean
2. 인터페이스를 이용한 어플리케이션 의존 관계 - 코드를 수정해야 하는 범위가 적어질 수는 있지만 코드에서 직접 의존하는 객체를 생성하는 문제점이 있다.
public interface MessageBean {
    public void say(String name);
} // MessageBean
public class Hello {
    public static void main(String[] args) {
        MessageBeanEn bean = new MessageBeanEn();
        bean.say("Spring");
        MessageBeanKo bean2 = new MessageBeanKo();
        bean2.say("스프링");
    } // main
} // Hello
package di2.com.tistory.gangzzang;

public class MessageBeanEn implements MessageBean {
    @Override
    public void say(String name) {
        System.out.println("Hello, " + name + " !");
    } // say
} // MessageBeanEn
public class MessageBeanKo implements MessageBean {
    @Override
    public void say(String name) {
        System.out.println("안녕하세요, " + name + " !");
    } // say
} // MessageBeanKo
3. DI 전달 방식 생성자 방식과 프로퍼티 설정 방식
public class FileEncryptor {
    prvate Encryptor encryptor;

    // 생성자를 통해서 의존 객체를 전달받음
    public FileEncryptor(Encryptor encryptor) {
    this.encryptor = encryptor;

    }
public class FileEncryptor {
    prvate Encryptor encryptor;

    // set 메서드를 통해서 의존 객체를 전달 받음
    public void setEnctyptor(Encryptor encryptor) {
    this.encryptor = encryptor;
    }
4. xml을 이용한 DI

Dao 인터페이스와 그 것을 구현한 OracleDao

public interface Dao {
    public void getList();
}
//인터페이스 구현을 통해 OracleDao를 만든다.
public class OracleDao implements Dao{
    @Override
    public void getList(){
        System.out.println("OracleDao.getList() - Oracle DB에 접근합니다.");
    }
}

Service인터페이스와 구현한 OracleService. Dao에 의존성이 있기 때문에 멤버로 Dao를 가지고 있다.

public interface Service {
    public void getList();
}
//역시 service또한 인터페이스 구현으로 만든다.
public class OracleService implements Service{
    Dao dao;
    @Override
    public void getList(){
        System.out.println("OracleService.getList() - OracleDao.GetList() 를 호출합니다.");
        dao.getList();
        System.out.println("OracleService.getList() - Dao에서 데이터를 받아 Controller에게 넘져줍니다.");
    }
    @Override
    public void setDao(Dao dao) {
        this.dao = dao;
    }
}
5. Service를 가지고 있는 Coltroller.
public class ListController {
    private Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doGet(){
        System.out.println("Controller - OracleService를 호출합니다.");
        service.getList();
        System.out.println("Controller - Service에서 데이터를 받아 View에 넘겨줍니다.");
    }
}
6. Controller를 호출할 메인 메소드.
mport org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class testMain {

    public static void main(String[] args) {
        Resource resource = new ClassPathResource("apllicationContext.xml");
        BeanFactory bf = new XmlBeanFactory(resource);
        ListController c = (ListController) bf.getBean("listController");
        System.out.println("Controller를 호출합니다.");
        c.doGet();
    }

}
7. 예상 결과
Controller를 호출합니다.
Controller - OracleService를 호출합니다.
OracleService.getList() - OracleDao.GetList() 를 호출합니다
OracleDao.getList() - Oracle DB에 접근합니다
OracleService.getList() - Dao에서 데이터를 받아 Controller에게 넘져줍니다.
Controller - Service에서 데이터를 받아 View에 넘겨줍니다
//이것을 실행 시키기 위해서는 설정된 xml파일이 필요하다.
//위의 세 클래스(controller, service, dao)를 이와 같이 등록한다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <bean id="oracleDao" class="com.OracleDao" >

    </bean>

    <bean id="oracleService" class="com.OracleService" >
        <property name="dao">
            <ref bean="oracleDao"/>
        </property>
    </bean>

    <bean id="listController" class="com.ListController" >
        <property name="service">
            <ref bean="oracleService"/>
        </property>
    </bean>

</beans>
8. Bean은 객체이다. Spring DI는 모든 등록된 클래스를 프로젝트를 처음 실행할때 전부 객채로 생성한다. 객체가 다른 객체를 필요로 할 때, 필요로 하는 객체를 생성자나 setter를 통해 주입시켜준다.
//xml파일에 <constructor-arg>를 이용하여 생성자를 이용한 DI도 가능하다.
//autowire속성으로 자동으로 의존성 주입이 가능하다.
<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean     id="service" 
            class="wakeup.di.service.Service">
    </bean>

    <bean     id="listController" 
            class="wakeup.di.controller.ListController"
            autowire="byType">
    </bean>
</beans>
<bean id="oracleDao" class="com.OracleDao" >

</bean>

<bean id="oracleService" class="com.OracleService" >
    <property name="dao">
        <ref bean="oracleDao"/>
    </property>
</bean>

<bean id="listController" class="com.ListController" >
    <property name="service">
        <ref bean="oracleService"/>
    </property>
</bean>
//xml파일의 id속성은 객체변수명이 되고, class는 클래스명이다.
OracleDao oracleDao = new OracleDao();

//property name은 멤버 Dao변수를 뜻하고, ref는 그 필드에 들어갈 객체이다.
OracleService oracleService = new OracleService();
oracleService.setDao(oracleDao);

//controller도 마찬가지.
ListController listController = new ListController();
listController.setOracleService(oracleService);

@Component

스프링은 특정 패키지 또는 그 하위 패키지에서 클래스를 찾아 스프링 빈으로 등록해주는 기능을 제공하고 있다.
package net.madvirus.spring4.chap02.shop;

import org.springframwork.stereotype.Compoenet;

@Component
public class OrderService {
....
}

package net.madvirus.spring4.chap02.shop;

import org.springframwork.stereotype.Compoenet;

@Component
public class ProductService {
...
@Resource(name = "productSerarchClientFactory")
public void setSerarchClientFactory(SearchClientFactory searchClientFactory){
    this.searchClientFactory = searchClientFactory;
}
// @Component 애노테이션을 적용했다면, <context:conponent-scan>태그를 이용해서 스프링이 클래스 검색할 패키지를 지정하면 된다. <context:conponent-scan>태그를 추가하면 스프링은 지정한 패키지에서 @Component 애노테이션이 적용된 클래스를 검색하여 빈으로 등록하게 된다.
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p"        
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 <context:component-scan base-package="madvirus.spring.chap04" scoped-proxy="no">
  <context:include-filter type="regex" expression="*HibernateRepository"/>
  <context:exclude-filter type="aspectj" expression="..*IBatisRepository"/>
 </context:component-scan>

</beans>

스프링 컨테이너가 클래스를 검색해서 스프링 빈으로 등록하기 때문에, 해당 빈의 생성자나 프로퍼티에 의존 객체를 전달하려면 @Autowired와 같은 애노테이션을 사용해야 한다. 예를 들어, 앞서 코드에서도 다음과 같이 @Resource 애노테이션을 이용해서 의존을 자동으로 연결하도록 하였다.

@ComponentScan 애노테이션을 사용하면 @Component 애노테이션이 붙은 클래스를 검색해서 빈으로 등록해준다.

@Configuration
#ComponentScan(basePackages ="net.madvirus.sprong4.chap02.shop")
public class ConfigScan {}
자동 검색된 빈의 이름과 범위
스프릉은 기본적으로 검색된 클래스를 빈으로 등록할 때, 클래스의(첫글자를 소문자로 바꾼)이름을 빈의 이름으로 사용한다. 예를 들어, 아래 코드의 경우 자동으로 등록되는 빈의 이름은 “productService”가 된다.
@Component
public class ProductService {
...
}
ApplicationContext를 사용하는 코드에서는 다음과 같이 “productsService”를 이용하여 스프링 컨테이너로부터 빈 객체를 가져오면 된다.
ProductService svc = context.getBean("productServce", ProdictService.class)
특정한 이름 명시
@Component("orderSvc")
public class OrderService{
...
}


Posted by 양형

댓글을 달아 주세요