[Spring] - DI(Dependency Injection) (복습)

2020. 8. 31. 22:02개발공부/Spring

728x90

#Spring #DI

지난 주에 이어 Spring 온라인 강의를 이어서 들었다. Spring의 특성을 자동화 이전 단계에서 직접 구현해보며 배우고 있다. 이번 시간엔 DI(Dependency Injection) 의존성 주입에 대해 배웠다. 주요 구현 방법은 객체들을 어떻게 주입시킬 것인가에 대한 것이다.

1. DI란

말그대로 의존성 주입인데, 홀로 존재하지 못하는 의존적이란 특성을 Spring Framework에 여러가지 방법으로 주입하여 객체를 생성해주는 것을 말한다. Spring에서 Container 내에 분포하는 클래스(Beans)는 의존적이다.

DI는 Spring 특징 중 하나인 IoC(Inversion of Control) 제어의 역행을 구현하는 하나의 갈래이다.

사용했을 때 장점은 유지보수가 편리하고 생산성이 높아진다. Java 코드를 건드리지 않고 <Beans> 설정 파일에서 객체 관련 정보를 수정할 수 있기 때문에 유지보수가 편하며, 하나씩 찾아다니며 변경하는 공수를 줄일 수 있다.

2. DI 방법

 1) 기본 객체 생성

  Java에서 객체를 생성하는 방법 그대로 쓰는 기본방식이다. 공통된 예시로 Hotel 객체가 Restaurant 객체에 의존적이고, Restaurant 객체는 Chef 객체에 의존적이다. 이 때 Hotel 객체의 reserveRestaurant 메서드를 실행하기 위해선 Main 클래스의 객체 생성은 아래와 같이 작성돼야 한다. 

기본 방법으로 객체를 생성할 때 발생할 수 있는 리스크는 객체 매개변수 누락이 있다. 또한 매번 객체를 생성해줘야 하기 때문에 번거로움도 수반된다.

public class Main {

	public static void main(String[] args) {
		
		//DI 처리 - 수작업 
		Hotel hotel = new Hotel(new Restaurant(new Chef()));
		hotel.reserveRestaurant();
		
	}

}

 2) 생성자를 이용한 객체 주입

 생성자와 Bean을 이용한 객체 주입 방법이다. 클래스의 의존된 객체가 매개변수로 지정된 생성자를 만들어 준 다음, src/main/resources 경로에 xml 설정 파일을 만들어 준다. 중요도가 높기 때문에 우선적으로 코드를 구현해주었다.

<bean id="[객체 호출 시 사용할 ID]", class="[경로]" />

<constructor-arg ref="[의존 객체의 bean id]" />

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"  
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.xsd">

	<bean id="test" class="com.spring.basic2.SpringTest" />
	
	<!-- Hotel객체의 의존객체들의 Bean 등록 및 의존성 주입설정, 생성자 참조 설정 -->
	<bean id="chef" class="com.spring.basic2.ex01.Chef"/>
	
	<bean id="res" class="com.spring.basic2.ex01.Restaurant">
		<constructor-arg ref="chef"/>
	</bean> 
	
	<bean id="hotel" class="com.spring.basic2.ex01.Hotel">
		<constructor-arg ref="res"/>
	</bean>



</beans>

[공통된 Main 클래스 DI 호출 코드]

package com.spring.basic2.ex01;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.springframework.context.support.GenericXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		
		
		GenericXmlApplicationContext ct = 
				new GenericXmlApplicationContext("classpath:test-config.xml");
		
		//호텔 객체를 만들어 주는데, ct에 저장된 getBean 메서드로 "id"를 호출한다.
		//의존된 객체들을 생성해준 적 없는데 자동으로 메서드가 동작한다. 이것이 바로 DI다.
		//나중에는 이런 코드들 안쓴다.
		Hotel hotel = ct.getBean("hotel", Hotel.class);
		hotel.reserveRestaurant();
		
		
	}

}

  3) Settter를 이용한 객체 주입

  2번의 생성자를 이용한 방법과 Bean을 생성한다는 점은 거의 흡사하지만, setter가 있어야 한다는 점과 xml 파일 객체 주입 태그를<property name="[Setter 필드]" ref="[의존 객체의 bean id]" />로 사용한다는 점이 다르다.

 Restaurant 클래스 내 setChef  입력 후 ctrl + space를 누르면 setter가 자동으로 생성된다. (setChef 카멜 케이스 유의) xml 파일 내에 윗 문단에 컬러링 된 구문과 동일하게 입력해주면 된다. 만일 set 값을 초기화 해주고 싶다면 value를 사용하면 된다.
<property name="[Setter 필드]" value="[Setter 값]" />

	<bean id="res" class="com.spring.basic2.ex01.Restaurant">
		<!--  <constructor-arg ref="chef"/>--> <!-- 생성자를 주입할 때 얘를 참조하렴 -->
		<property name="chef" ref="chef" />
	</bean> 

3. 의존 객체 자동 주입

 Spring MVC에서 사용하는 방법이며 아노테이션(Annotation)을 사용하여 데이터 타입이 맞는 객체가 있으면 자동으로 객체가 주입된다. 이 방법 또한 xml 파일 생성 및 <Bean> 태그 입력은 동일하나, <constructor> 나 <property> 태그가 불필요 하다는 점에서 편리성이 돋보인다. 다만 xml 파일 내 최초 설정해줘야 되는 부분이 두 군데 있다. xmlns(namespace) 부분과 <context:annotation-config /> 태그다.

<?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-4.3.xsd
">

<!--  자동 스캔 명령 추가 -->

<context:annotation-config />


<bean id="paper" class="com.spring.basic.ex04.Paper" />
<bean id="printer" class="com.spring.basic.ex04.Printer" />


</beans>

4. 그 외 알아두면 좋은 것들

  싱글톤 방식 : Spring은 객체가 하나만 생성되는 싱글톤 방식을 채택했다. 만일 Person 객체를 A, B 두 사람 것으로 각각 만들고 싶다면 <bean> 태그 내에 scope="prototype"을 설정해주면 된다. 그럼 각 객체별로 setter를 달리 지정해줄 수 있다. 간략한 예시 투척

<bean id="person" class="com.spring.basic.ex03.Person" scope="prototype">
	<property name="name" value="홍길동" />
	<property name="age" value="20" />
</bean>
public static void main(String[] args) {
		
		GenericXmlApplicationContext ct = 
				new GenericXmlApplicationContext("classpath:prototype-config.xml");
		
		Person hong = ct.getBean("person", Person.class);
		Person kim = ct.getBean("person", Person.class);
		
		System.out.println("============================");
		kim.setName("김개똥");
		kim.setAge(11);
		System.out.println("hong의 이름: " + hong.getName());
		System.out.println("hong의 나이: " + hong.getAge());
		System.out.println("kim의 이름: " + kim.getName());
		System.out.println("kim의 나이: " + kim.getAge());
	}