Spring

Spring Framework - Dependency Injection

728x90

참조한 강의 : Spring & Hibernate For Beginners (www.udemy.com/course/spring-hibernate-tutorial/)

 

 

- Dependency Injection

 

https://www.youtube.com/watch?v=IKD2-MAkXyQ

Dependency Injection : https://www.youtube.com/watch?v=IKD2-MAkXyQ

Dependency Injection, 직역을 하면 의존성 주입 이다. 

"의존성" 이란게 뭘 말하는 걸까?

위의 영상을 보면 Model 과 Database 가 나오는데, "데이터베이스로 부터 데이터를 얻어오기 위해서는 Model 클래스가 DB 에게 fetch 요청을 보내야 얻어올 수 있다" 고 나온다. 

이는 다시말하면, Model 클래스가 DB 에 의존하고 있다고 말할 수 있다. (DB 가 있어야만 데이터를 얻어 올 수 있기 때문임)

이렇게 어떤 클래스가 또 다른 클래스가 제공하는 기능에 의존하고 있을때 의존성이 있다고 말한다.

그리고 Model 클래스 처럼 어떤 클래스의 기능에 의존하는 객체를 client 라 부르고, 기능을 제공하는 클래스를 service 라 부른다 (참조 : en.wikipedia.org/wiki/Dependency_injection

(spring 에서 보통 기능을 제공하는 클래스들을 ~~Service 이런식으로 이름을 붙여서 사용하는게 일반적이다)

 

 

코드를 직접 작성해보면서 더 자세히 dependency injection 에 대해 이해해보자

(앞선 포스팅 sdy-study.tistory.com/167?category=995506 에 작성한 코드의 연장선이니 앞으로 작성할 코드에 대한 상세한 설명은 이 포스팅을 참조 바람)

 

 

 

- 3가지 Dependency Injection 방법

Dependency Injection 을 Spring Framework 에서 구현하는 방법은 3가지가 존재한다.

 

첫번째로, 생성자를 통해서 의존성 주입하는 방법

두번째로, Setter method 를 이용해서 의존성 주입하는 방법

세번째로, properties 파일을 통해서 의존성 주입하는 방법

 

이렇게 3가지로 나뉜다.

 

1. 생성자를 통한 의존성 주입

첫번째로, 생성자를 통해서 의존성 주입을 하는 방법 부터 알아보자

먼저 앞선 포스팅에서 다음과 같은 5개의 파일들을 만들었었다.

 

- Coach.java

public interface Coach {
    public String getDailyWorkout();
}
 
cs

 

 

 

- BaseballCoach.java

public class BaseballCoach implements Coach{
    @Override
    public String getDailyWorkout() {
        return "Spend 30 Minutes on batting practices";
    }
}
cs

 

 

- TrackCoach.java

public class TrackCoach implements Coach {
    @Override
    public String getDailyWorkout() {
        return "Run a hard 5k";
    }
}
 
cs

 

 

- HelloSpringApp.java

package com.luv2code.springdemo;
 
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class HelloSpringApp {
    public static void main(String[] args) {
        // load the spring configuration file
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // retrieve bean from spring container
        Coach theCoach = context.getBean("myCoach", Coach.class);
    
        // call methods on the bean
        System.out.println(theCoach.getDailyWorkout());
        
        // close the context
        context.close();
    }
}
 
cs

 

 

- applicationContext.xml

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- Define your beans here -->
    <bean id="myCoach" class="com.luv2code.springdemo.BaseballCoach">
    </bean>
</beans>
cs

 

생성자를 통해서 의존성을 주입하는 방법은 

1. 의존성을 주입할 새로운 클래스 또는 인터페이스를 만든다

2. 1번에서 만든 클래스 또는 인터페이스의 기능을 사용할 클래스 부분에 생성자를 추가로 정의한다.

3. applicationContext.xml 파일에 의존성이 주입되었음을 명시한다.

 

이렇게 3단계로 나뉜다.

이를 구현하기 위해서 FortuneService 라는 인터페이스를 하나 만들어서 운을 알려주는 메소드를 하나 구현하고 이를 의존성 주입 시켜보자. 그리고 이 인터페이스를 구현할 HappyFortuneService.java 파일을 만들어서 클래스 하나를 생성해주자.

 

 

- FortuneService.java

public interface FortuneService {
    public String getFortune();
}
 
cs

 

 

 

- HappyFortuneService.java

public class HappyFortuneService implements FortuneService {
    private String[] fortunes = {"so so""What a nice day""Totally messed up today"};
    
    @Override
    public String getFortune() {
        int idx = (int)(Math.random() * 3);
        return fortunes[idx];
    }
}
cs

 

 

 

그리고 이 인터페이스의 getFortune 기능을 가져오기 위해서 생성자로 의존성 주입을 해볼건데

BaseballCoach.java 파일에 다음과 같이 추가해준다.

 

 

- BaseballCoach.java

public class BaseballCoach implements Coach{
    // define private field for dependency
    private FortuneService fortuneService;
    
    public BaseballCoach(FortuneService fortuneService) {
        this.fortuneService = fortuneService;
    }
        
    @Override
    public String getFortune() {
        // use my fortuneService to get a fortune.
        return fortuneService.getFortune();
    }
}
cs

다음으로 applicationContext.xml 에 의존성 주입을 명시하기 위해 아래 부분을 추가한다.

 

 

- applicationContext.xml

//  나머지 위아래 부분 생략
<bean id="myFortuneService" class="com.luv2code.springdemo.HappyFortuneService">
</bean>
        
<bean id="myCoach" class="com.luv2code.springdemo.BaseballCoach">
      <!-- set up constructor injection -->
       <constructor-arg ref="myFortuneService"/>
</bean>
cs

 

 

생성자를 통한 의존성 주입은 이와 같은 단계로 구성되며,

의존하게될 대상 클래스 또는 인터페이스는 private 으로 선언하고, 생성자로 초기화 한다는 점

그리고, xml 파일 부분 설정할때, Service 부분 먼저 bean 으로 선언하고, 이 service 를 사용할 bean 의 내부에 constructor-arg 태그를 사용한다는 점에 주의하면 된다.

 

 

2. setter method 를 이용한 의존성 주입

setter method 를 통해서 의존성을 주입하는 방법은 다음과 같은 절차로 이뤄진다

 

1. setter method 를 생성하기

2. applicationContext.xml 에 setter method 를 통해 의존성을 주입하는것을 명시하기

 

(생성자로 처리하는 방법과 사실 거의 유사하다)

 

setter method 로 의존성 주입하는 것을 테스트 해보기 위해, CricketCoach 라는 새로운 클래스를 하나 생성해보자

 

 

- CricketCoach.java

public class CricketCoach implements Coach {
    
    private FortuneService fortuneService;
    
    public CricketCoach() {}
 
    @Override
    public String getDailyWorkout() {
        return "Practice fast bowling for 15 minutes";
    }
 
    @Override
    public String getFortune() {
        return "Cool !" + fortuneService.getFortune();
    }
 
    public void setFortuneService(FortuneService fortuneService) {
        this.fortuneService = fortuneService;
    }
 
}
cs

여기서도 역시 Service 를 private 으로 선언했고, 이에 대한 초기화 작업은 setFortuneService() 를 통해서 이뤄진다.

 

다음 applicationContext.xml 은 이렇게 추가해준다

 

 

- applicationContext.xml

<bean id="myCricketCoach" class="com.luv2code.springdemo.CricketCoach">     
    <property name="fortuneService" ref="myFortuneService"/>
</bean>
cs

여기서는 property 태그를 이용해서 처리해준다는게 차이점이다

(name 은 이 의존성을 사용하고 있는 CricketCoach.java 에서 선언한 private FortuneService fortuneService 의 이름이며, ref 는 의존하고 있는 대상 bean 을 의미한다)

 

 

3. Literal values injection.

literal value 는 앞서 봤던 클래스나 인터페이스 같은게 아니라, 숫자형 변수, 문자형 변수 같이 자바에 내장된 기본 자료형 변수 값들을 의미한다.

앞서 봤던것처럼, 단순히 클래스나 인터페이스만 의존성 주입을 하는게 아니라, 그냥 숫자를 넣을 수도 있고, 문자를 넣을 수도 있다.

 

예를들어, 앞서 봤던 CricketCoach.java 에서 이 코치의 이메일값 이나 담당하고 있는 팀이름, 코치의 이름 등 여러가지 변수를 넣어서 설정하고 싶을 수도 있다.

 

이때는 아래의 3단계 과정으로 처리된다.

1. 확장자명이 properties 인 파일을 생성한다

2. applicationContext.xml 파일에서 1번의 파일을 로드하도록 설정한다.

3. 1번의 파일로 부터 참조하도록 bean 태그 내부에 property 태그를 설정한다.

 

먼저 properties 파일 부터 만들어보자

이름을 sport.properties 라 짓고 프로젝트 내부의 applicationContext.xml 과 같은 위치에 생성해줘야 한다 

(패키지 내부가 아닌 프로젝트 내부임)

 

 

- sport.properties

foo.email=test@gmail.com
foo.team=royal team
cs

해당 코치의 이메일 주소, 그리고 팀 이름만 설정해 본다

그리고 xml 파일에 아래같이 추가한다.

 

 

- applicationContext.xml

<bean id="myCricketCoach" class="com.luv2code.springdemo.CricketCoach">
       <property name="fortuneService" ref="myFortuneService"/>
       
       <property name="emailAddress" value="${foo.email}" />
       <property name="team" value="${foo.team}"/>
</bean>
cs

 

 

앞서 설정했던 myCricketCoach 부분에 property 를 추가한것이다.

value 를 ${} 형식으로 설정해야 된다는것에 유의해야 한다.

 

그리고 CricketCoach 에 이 value 들을 받아줄수 있는 private 변수들과 setter, getter 메소드를 선언한다.

 

 

- CricketCoach.java

public class CricketCoach implements Coach {
    
    private FortuneService fortuneService;
    private String emailAddress;
    private String team;
    
    public String getEmailAddress() {
        return emailAddress;
    }
 
    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
 
    public String getTeam() {
        return team;
    }
 
    public void setTeam(String team) {
        this.team = team;
    }
 
    public CricketCoach() {}
 
    @Override
    public String getDailyWorkout() {
        return "Practice fast bowling for 15 minutes";
    }
 
    @Override
    public String getFortune() {
        return "Cool !" + fortuneService.getFortune();
    }
 
    public void setFortuneService(FortuneService fortuneService) {
        this.fortuneService = fortuneService;
    }
 
}
 
cs

 

여기까지 xml 파일을 이용해서 IoC 와 Dependency Injection 에 대해서 배워봤다.

 

다음에 다룰 포스팅은 spring bean scope 와 life cycle 에 대해 알아본다.

 

 

 

- reference)

Concepts of Dependency Injection : en.wikipedia.org/wiki/Dependency_injection

 

 

 

 

728x90