오봉이와 함께하는 개발 블로그
Spring - 의존성, DI(Dependency Injection) 본문
728x90
의존성(Dependency)
- 객체 간 의존성
- 한 클래스가 다른 클래스의 객체를 통해 그 클래스의 메소드를 실행할 때 이를 '의존'한다고 표현
class A {
private B b;
public A() {
b = new B();
}
}
// A 클래스에서 직접 생성
// (A 클래스 객체 생성될 때 B 클래스 객체 b 생성)
// (일체형으로 묶여 있음)
class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
// 외부에서 만든 객체를 받아서 사용
// (부품을 조립하여 사용하는 개념)
// DI
- DI (Dependency Injection : 의존성 주입)
- 외부에서 빈(객체)을 만들어 필요로 하는 곳에 전달해 주도록 하는 것
- 즉, 개발자가 new 연산자를 사용하여 직접 객체를 생성하지 않고 외부에서 생성된 Bean(객체)을 IoC 컨테이너가 넣어 주는 방식 (주입 : injection)
- 일반적으로 부품(Bean)을 조립(의존성 주입)해서 사용한다고 함
- DI(의존성 주입) 방법을 사용하는 이유
- 의존하는 객체의 클래스가 변경되거나 다른 클래스의 객체를 사용하게 될 경우 의존 관계(결합 상태)에 있는 다른 모든 클래스들의 소스 코드도 변경해야 함
- 의존성 주입 방법을 사용하면, 클래스 결합 상태를 변경하거나 객체를 주입하는 부분만 수정하면 되므로 수정할 코드의 양을 줄일 수 있다는 장점이 있다.
- 예
- A1 클래스를 사용하다가 A2 클래스로 변경할 경우 설정 파일에서 클래스 이름만 변경해 주면 됨
- <bean id=”a” class=”com.package.A1” /> // A1을 A2로 변경하기만 하면 됨
- 스프링에서 의존성 주입 방법
- XML을 이용한 방법
- xml 설정 파일에 <bean> 설정
- Annotation을 이용한 방법
- 자바 코드에서 '@어노테이션'으로 설정
- XML을 이용한 방법
- 스프링을 사용하지 않는 DI
- DI를 사용하지 않는 코드
- DI를 사용하지 않는 코드 특징
- 의존성 관계가 있는 객체를 개발자가 new 연산자를 이용해서 직접 생성
- DI를 사용하지 않는 코드 특징
- DI를 사용한 코드
- 생성자 기반 DI
- Setter 기반 DI
- DI를 사용하지 않는 코드
- 스프링 DI
- XML을 이용한 DI
- Annotation을 이용한 DI
스프링을 사용하지 않는 DI - DI를 사용하지 않는 코드 예제 - NameService.java, NameController.java, NameMain.java
- NameService
- NameController
- new 연산자를 사용해서 NameService 클래스 객체 생성
- 의존성 존재
- DI는 아님
- NameMain
package com.di.no_spring_no_di;
public class NameService {
// 이름 전달 받아서 "내 이름은 ~ 입니다" 반환하는 메소드
public String showName(String name) {
System.out.println("NameService showName() 메소드");
String myName = "내 이름은 " + name + " 입니다.";
return myName;
}
}
package com.di.no_spring_no_di;
public class NameController {
// 원하는 곳에서 new 연산자를 사용해서 객체 직접 생성
// 의존성은 존재하나 DI는 아님
NameService nameService = new NameService();
// 이름을 전달 받아서 NameService 클래스의 showName() 메소드에게 전달하고
// "내 이름은 ~ 입니다" 전달 받아서 출력하는 메소드.
public void show(String name) {
System.out.println("NameController : " + nameService.showName(name));
}
}
package com.di.no_spring_no_di;
public class NameMain {
public static void main(String[] args) {
NameController nameController = new NameController();
nameController.show("홍길동");
}
}
스프링을 사용하지 않는 DI - 생성자 기반 DI 코드 예제 - NameService.java, NameController.java, NameMain.java
- 의존성 관계에 있는 객체를 new를 통해 직접 생성하지 않고
- 생성자를 통해 외부에서 전달 (주입 : injection)
package com.di.no_spring_di_constructor;
public class NameService {
// 이름 전달 받아서 "내 이름은 ~ 입니다" 반환하는 메소드
public String showName(String name) {
System.out.println("NameService showName() 메소드");
String myName = "내 이름은 " + name + " 입니다.";
return myName;
}
}
package com.di.no_spring_di_constructor;
public class NameController {
// 멤버 변수 선언
NameService nameService;
// NameService 객체를 new 연산자를 통해 직접 만들지 않고 생성자를 통해 객체를 전달 받음.
// 생성자를 통해 외부에서 주입
// 의존성 주입
public NameController(NameService nameService) {
this.nameService = nameService;
}
public void show(String name) {
System.out.println("NameController : " + nameService.showName(name));
}
}
package com.di.no_spring_di_constructor;
public class NameMain {
public static void main(String[] args) {
// NameController에 전달할 NameService 클래스 객체 생성
NameService nameService = new NameService();
NameController controller = new NameController(nameService);
controller.show("이몽룡");
}
}
스프링을 사용하지 않는 DI - Setter 기반 DI - NameService.java, NameController.java, NameMain.java
- 생성자를 구현할 때 전달해야 할 객체가 많은 경우 일일이 생성자를 구현하는 것이 번거로울 수가 있음 이 때 Setter 메소드를 사용하여 의존성 주입 수행
package com.di.no_spring_di_setter;
public class NameService {
// 이름 전달 받아서 "내 이름은 ~ 입니다" 반환하는 메소드
public String showName(String name) {
System.out.println("NameService showName() 메소드");
String myName = "내 이름은 " + name + " 입니다.";
return myName;
}
}
package com.di.no_spring_di_setter;
public class NameController {
NameService nameService;
// 생성자 없고 Setter 메소드를 통해서 외부에서 주입 받음
public void setNameService(NameService nameService) {
this.nameService = nameService;
}
public void show(String name) {
System.out.println("NameController : " + nameService.showName(name));
}
}
package com.di.no_spring_di_setter;
public class NameMain {
public static void main(String[] args) {
NameService nameService = new NameService();
NameController controller = new NameController();
// Setter메소드를 통해 nameService 객체 전달
controller.setNameService(nameService);
controller.show("성춘향");
}
}
Spring DI
- 스프링의 의존성 주입 (스프링의 핵심 기능)
- 스프링 프레임워크는 컨테이너 역할을 하기 때문에 스프링 컨테이너 또는 IoC 컨테이너라고도 함
- 스프링은 필요한 빈을 생성하여 컨테이너에 넣어 관리
- 따라서 빈을 생성하기 위한 설정과 의존 객체를 주입시키기 위한 설정 필요
IoC 컨테이너
- Inversion of Control : 제어의 역전
- 객체를 생성/관리하는 제어권이 개발자에게 있는 것이 아니라
- 객체를 생성해서 주입시키고 관리하는 제어권이 스프링에게 있기 때문에 제어가 역전되었다고 하는 것
- 객체를 개발자가 생성/관리하는 것이 아니라 IoC 컨테이너가 빈으로 설정/주입 관리
- 스프링은 프레임워크이면서 동시에 컨테이너 역할
- 빈을 생성하고 소멸하기까지 생명주기를 관리하기 위해 빈을 컨테이너에 로드하여 관리
XML을 이용한 DI (생성자 기반)
- XML 파일에 빈(bean : 부품)을 정의(생성)하고
- <bean id=”이름” class=”패키지명.클래스명”>
- 의존성 설정 (부품 조립)
- <ref bean=”의존하는 빈”>
- XML을 이용해서 의존성을 주입하기 위해서는 클래스에 생성자 또는 Setter 메소드가 반드시 있어야 함
- 스프링은 Pre-loading 방식 사용
- ApplicationContext를 이용해서 컨테이너를 구동하면 컨테이너가 구동되는 시점에 스프링 설정 파일에 등록된 빈을 생성하고 컨테이너에 로드
XML을 이용한 DI - 생성자 기반 DI (스프링 DI) 예제 - applicationContext.xml, NameService.java, NameController.java, NameMain.java
- 클래스에 생성자 있어야 하고
- 스프링 설정 파일(xml)에서 빈을 정의할 때
- <constructor-arg ref=”의존하는 빈”> 태그를 이용하여 의존성 주입
- NameService 객체를 NameMain에서 생성하지 않고
- XML 설정 파일에서 생성
- Main 클래스 역할
- 컨테이너 객체 생성
- 컨테이너에서 컴포넌트(빈) 가져옴
<bean id="nameService" class="com.di.spring_di_xml_constructor.NameService"/>
<bean id="nameController" class="com.di.spring_di_xml_constructor.NameController">
<!-- 생성자 기반 의존성 주입 : nameService 참조-->
<constructor-arg ref="nameService"/>
</bean>
package com.di.spring_di_xml_constructor;
public class NameService {
// 이름 전달 받아서 "내 이름은 ~ 입니다" 반환하는 메소드
public String showName(String name) {
System.out.println("NameService showName() 메소드");
String myName = "내 이름은 " + name + " 입니다.";
return myName;
}
}
package com.di.spring_di_xml_constructor;
public class NameController {
// 멤버 변수 선언
NameService nameService;
// NameService 객체를 new 연산자를 통해 직접 만들지 않고 생성자를 통해 객체를 전달 받음.
// 생성자를 통해 외부에서 주입
// 의존성 주입
public NameController(NameService nameService) {
this.nameService = nameService;
}
public void show(String name) {
System.out.println("NameController : " + nameService.showName(name));
}
}
package com.di.spring_di_xml_constructor;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class NameMain {
public static void main(String[] args) {
AbstractApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
NameController controller = context.getBean("nameController", NameController.class);
controller.show("변학도");
context.close();
}
}
XML을 이용한 DI - 생성자 기반 DI (스프링 DI) 예제 - applicationContext.xml, Speaker.java, Tv.java, TvMain.java
<bean id="speaker" class="com.di.spring_di_xml_constructor_ex1.Speaker"/>
<bean id="tv" class="com.di.spring_di_xml_constructor_ex1.Tv">
<!-- 생성자 기반 의존성 주입 : nameService 참조-->
<constructor-arg ref="speaker"/>
</bean>
package com.di.spring_di_xml_constructor_ex1;
public class Speaker {
public void volumeUp() {
System.out.println("볼륨을 키웁니다.");
}
public void volumeDown() {
System.out.println("볼륨을 낮춥니다.");
}
}
package com.di.spring_di_xml_constructor_ex1;
public class Tv {
Speaker speaker;
public Tv (Speaker speaker) {
this.speaker = speaker;
}
public void Up() {
speaker.volumeUp();
}
public void Down() {
speaker.volumeDown();
}
}
package com.di.spring_di_xml_constructor_ex1;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TvMain {
public static void main(String[] args) {
AbstractApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
Tv tv = context.getBean("tv", Tv.class);
tv.Up();
tv.Down();
context.close();
}
}
XML을 이용한 DI (Setter 기반)
- 클래스에 반드시 Setter 메소드가 있어야 함
- 스프링 설정 파일(xml)에서 <property> 태그 이용하여 의존 객체 주입
- <bean id=”nameService” class=”...” />
- <property name=”nameService” ref=”nameService” />
- name : setter 메소드 이름
- setNameService()를 이용해서 객체를 주입하겠다는 의미
- ref : 참조 객체(빈) 이름
- name : setter 메소드 이름
- setter 메소드를 사용할 때는 기본 생성자 외에 다른 생성자(매개변수가 있는 생성자)를 정의해서는 안 됨
Spring DI - XML을 이용한 DI - Setter 기반 DI 예제 - applicationContext2.xml, NameService.java, NameController.java, NameMain.java
<bean id="nameService" class="com.di.spring_di_xml_setter.NameService" />
<bean id="nameController" class="com.di.spring_di_xml_setter.NameController">
<!-- Setter 기반 의존성 주입 : name(setter 메소드 이름 : setNameService()), ref(의존성있는 빈) -->
<property name="nameService" ref="nameService" />
</bean>
package com.di.spring_di_xml_setter;
public class NameService {
// 이름 전달 받아서 "내 이름은 ~ 입니다" 반환하는 메소드
public String showName(String name) {
System.out.println("NameService showName() 메소드");
String myName = "내 이름은 " + name + " 입니다.";
return myName;
}
}
package com.di.spring_di_xml_setter;
public class NameController {
NameService nameService;
// 생성자 없고 Setter 메소드를 통해서 외부에서 주입 받음
public void setNameService(NameService nameService) {
this.nameService = nameService;
}
public void show(String name) {
System.out.println("NameController : " + nameService.showName(name));
}
}
package com.di.spring_di_xml_setter;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class NameMain {
public static void main(String[] args) {
AbstractApplicationContext context = new GenericXmlApplicationContext("applicationContext2.xml");
NameController controller = context.getBean("nameController", NameController.class);
controller.show("강길동");
context.close();
}
}
Spring DI - XML을 이용한 DI - Setter 기반 DI - 연습문제 - applicationContext2.xml, Speaker.java, Tv.java, TvMain.java
<bean id="speaker" class="com.di.spring_di_xml_setter_ex1.Speaker" />
<bean id="tv" class="com.di.spring_di_xml_setter_ex1.Tv">
<!-- Setter 기반 의존성 주입 : name(setter 메소드 이름 : setNameService()), ref(의존성있는 빈) -->
<property name="speaker" ref="speaker" />
</bean>
package com.di.spring_di_xml_setter_ex1;
public class Speaker {
public void volumeUp() {
System.out.println("볼륨을 키웁니다.");
}
public void volumeDown() {
System.out.println("볼륨을 낮춥니다.");
}
}
package com.di.spring_di_xml_setter_ex1;
public class Tv {
Speaker speaker;
public void setSpeaker(Speaker speaker) {
this.speaker = speaker;
}
public void Up() {
speaker.volumeUp();
}
public void Down() {
speaker.volumeDown();
}
}
package com.di.spring_di_xml_setter_ex1;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TvMain {
public static void main(String[] args) {
AbstractApplicationContext context = new GenericXmlApplicationContext("applicationContext2.xml");
Tv tv = context.getBean("tv", Tv.class);
tv.Up();
tv.Down();
context.close();
}
}
XML을 이용한 DI (생성자 기반 데이터 설정)
- <constructor-arg value=”홍길동” /> // 값 설정
- 매개변수가 있는 생성자에게 값 전달
Spring DI - XML 기반 - 생성자 기반 - 데이터 설정 - applicationContext2.xml, BMI.java, Member.java, MemberMain.java
<bean id="bmi" class="com.di.spring_di_xml_constructor_value.BMI"/>
<bean id="member" class="com.di.spring_di_xml_constructor_value.Member">
<!-- 생성자 기반 의존성 주입 -->
<constructor-arg ref="bmi"/>
<!-- 매개변수 있는 생성자 사용하므로 값 설정 -->
<constructor-arg value="홍길동" />
<constructor-arg value="24" />
<constructor-arg value="177" />
<constructor-arg value="59" />
<constructor-arg><!-- ArrayList 값 설정-->
<list>
<value>수영</value>
<value>헬스</value>
<value>에어로빅</value>
</list>
</constructor-arg>
</bean>
package com.di.spring_di_xml_constructor_value;
public class BMI {
public void showBMI(float height, float weight) {
float bmi = weight/(height * height) * 10000;
String result;
if(bmi >= 25) {
result = "비만입니다.";
}
else if(bmi < 25 && bmi >= 23) {
result = "과체중입니다.";
}
else if(bmi < 23 && bmi >= 18.5) {
result = "정상";
}
else {
result = "저체중";
}
System.out.println("BMI 지수 : " + bmi + " - " + result);
}
}
package com.di.spring_di_xml_constructor_value;
import java.util.ArrayList;
public class Member {
private BMI bmi;
private String name;
private int age;
private float height;
private float weight;
private ArrayList<String> courses;
// 생성자 기반 DI
public Member(BMI bmi, String name, int age, float height, float weight, ArrayList<String> courses) {
this.bmi = bmi;
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
this.courses = courses;
}
public BMI getBmi() {
return bmi;
}
public void setBmi(BMI bmi) {
this.bmi = bmi;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
public ArrayList<String> getCourses() {
return courses;
}
public void setCourses(ArrayList<String> courses) {
this.courses = courses;
}
public void showBMI() {
bmi.showBMI(height, weight);
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", weight=" + weight +
", courses=" + courses +
'}';
}
}
package com.di.spring_di_xml_constructor_value;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class MemberMain {
public static void main(String[] args) {
AbstractApplicationContext context = new GenericXmlApplicationContext("applicationContext2.xml");
Member member = context.getBean("member", Member.class);
System.out.println(member); // toString 자동 호출
member.showBMI();
context.close();
}
}
728x90
'BE > Spring' 카테고리의 다른 글
Spring - Controller와 요청 처리 (0) | 2022.01.07 |
---|---|
Spring - 모델 패턴 (0) | 2022.01.06 |
Spring - AOP 간단한 기초 설명 (0) | 2022.01.05 |
Spring - 스프링 싱글톤 & 어노테이션을 통한 DI (0) | 2022.01.05 |
Spring - 스프링 개요 (0) | 2022.01.04 |
Comments