Dependency Injection
DI(Dependency Injection) 개념
1. DI(Dependency Injection)란?
간단히 말해서 객체를 setter로 받아서 작업하는것 이라고 생각하면 됩니다.
자세히 말하자면?
우선 단어번역부터 시작해봅니다
DI를 번역하자면 의존성 주입이라고 해석됩니다. 이게 무슨소리인가?
2. 의존성이란?
사용할 작업 클래스에 의존하는 성격을 말합니다
보통 사람이 다른사람에게 의존한다, 의지하다, 종속적이다 이러한 개념이 있습니다.
다른사람이 변한다면, 자기 자신도 변하게 되는걸 말합니다.
클래스 또한 다른 클래스에 영향을 많이 받을때, 의존성이 높다고 말합니다.
public class ClassA{
public void show(String str){
System.out.println("ClassA : " + str);
}
}
public class ClassB{
public void show(String str){
System.out.println("ClassB : " + str);
}
}
public class WorkClass{
public void showMain(){
ClassA work = new ClassA();
work.show("hello!")
}
}
위의 WorkClass는 ClassA 클래스에 매우 의존적이라고 할 수 있습니다.
무슨말이냐?
WorkClass는 ClassA 의 작업밖에 하지 못합니다
다른작업을 하고싶으면?
ClassA의 내용을 바꾸거나, 다른클래스를 생성해서 작업해야합니다.
ClassB의 일을 하려면 아래처럼 객체를 새로 생성하고, 이를 사용하는 작업을 해야합니다.
public class WorkClass{
public void showMain(){
ClassB work_b = new ClassB();
work_b.show("hello!")
}
}
처럼 말입니다.
OOP(객체지향)의 개념 중에 "객체를 부품처럼 사용한다"가 있습니다.
다른 기능을 사용하고 싶다면 바꿔 껴서 사용하면 된다는 개념입니다.
하지만 위의 예제에서 WorkClass가 다른 기능을 사용하려면
객체내용을 바꾸거나, 객체를 새로 생성하거나, 클래스의 내용을 바꿔야합니다.
이를 의존성이 높다고 말합니다.
3. 의존성을 낮춰보자.
그렇다면, 객체지향에 맞게 의존성을 좀 낮춰보고 싶다.
다른기능을 쓰기 좀 편하게 하고싶다.
해서 사용하게 되는게 interface 입니다.
public interface ClassInter{
void show(String str);
}
public class ClassA implements ClassInter{
@Override
public void show(String str){
System.out.println("ClassA : " + str);
}
}
public class ClassB implements ClassInte{
@Override
public void show(String str){
System.out.println("ClassB : " + str);
}
}
public class WorkClass{
public void showMain(){
ClassInter inter = new ClassA();
inter.show("hello");
}
}
자 이제 메인 클래스의 ClassInter inter 객체가 다른 작업을 하고싶으면?
ClassA 클래스 대신 다른 작업할 클래스를 끼워넣으면됩니다.
ClassB의 작업을 하고싶으면 아래처럼 그냥 ClassB를 끼워넣으면 됩니다
public class WorkClass{
public void showMain(){
ClassInter inter = new ClassB();
inter.show("hello");
}
}
보다 의존성이 낮아진것 같습니다.
WorkClass를 수정해야 할 부분도 적고, 기능 클래스가 좀 더 부품같이 느껴집니다.
새로운 작업을 하고싶으면? 그냥 새로운 객체를 붙이면 됩니다.
그러나 아직까지 문제가 남아있습니다.
WorkClass 클래스가 이번엔 ClassB 클래스에 의존적이게 됩니다.
즉, 저렇게 내부에서 생성한 클래스로만 작업이 가능한 겁니다.
ClassC를 작업하고 싶은데, 그럴려면 WorkClass의 내용을 바꿔야만 하는 사태가 발생합니다.
public class WorkClass{
public void showMain(){
ClassInter inter = new ClassC();
inter.show("hello");
}
}
이처럼 내용을 수정해야합니다.
4. 의존 클래스를 변수로 받자 DI(Dependency Injection)
자, 이제 생각합니다. 내부에서 클래스를 만들면 의존성이 높아진다는걸 깨닫습니다
그럼 어떻게 작업할것인가?
클래스를 외부에서 만들어지던지, 하늘에서 떨어지던지 상관없다. 내부에서만 안만들어지면 되는구나
그럼 외부로부터 변수로 받자!
해서 생긴 개념을 의존성 주입 이라고 하며, DI(Dependency Injection) 라고 합니다.
public interface ClassInter{
void show(String str);
}
public class ClassA implements ClassInter{
@Override
public void show(String str){
System.out.println("ClassA : " + str);
}
}
public class ClassB implements ClassInte{
@Override
public void show(String str){
System.out.println("ClassB : " + str);
}
}
public class WorkClass{
private ClassInter inter;
public void setInter(ClassInter inter) {
this.inter = inter;
}
public void showMain(){
inter.show("hello");
}
}
자, WorkClass를 보면 객체를 생성하는 new가 하나도 없습니다
setter를 통해 클래스객체를 받고
이를 showMain 메소드에서 기능을 실행합니다
언제든지 어떤 클래스든지
ClassInter 인터페이스를 implements 받는 클래스라면
해단 클래스를 사용할 수 있습니다.
자 이제 이 프로그램을 실행해보기로 합니다.
public class WorkMain{
public static void main(String[] args){
WorkClass work = new WorkClass();
ClassInter inter = new ClassA();
work.setInter(inter);
work.showMain();
}
}
어?! 뭔가 이상해졌습니다
우리는 지금까지의 "new로 객체 생성을 하지 않으면 의존성을 줄일 수 있다"고 결론을 짓고,
new가 없는 클래스를 만들어냈습니다.
그런데 결국 메인클래스 WorkMain에서 new로 객체 생성을 하게되었습니다.
메인클래스 WorkMain가 작업클래스 WorkClass 클래스에 대해 의존성이 높아졌다는걸 의미합니다.
이제 난관에 봉착했습니다.
분명 어디선가는 객체를 생성해야 하고, 객체를 생성하면 의존성은 높아진다.
객체생성, 의존성 낮추기. 두마리 토끼를 다 잡을 생각을 해야 합니다.
5. Spring에서의 DI
이 두마리 토끼를 Spring Framework 에서는 이렇게 해결합니다.
그렇다면, Main클래스에도 객체를 주입해서 실행하게 하자.
그게 무슨말인가?
따로 주입용 클래스 관리 파일을 만들자.
이 파일은 클래스를 생성하게 할 수 있고,
생성하는 클래스에 변수를 주입할 수도 있고,
따로 가져올 수도 있는 기능을 가진
관리 파일을 만들고,
이 관리파일을 메인 클래스에 삽입 시켜서 의존성을 낮춰보자
이를 Spring Bean Configuration File이라고 합니다
형식은 xml의 형식을 따르며, 짧게 설정 파일 이라고 합니다
객체를 생성하고, 변수를 삽입하는 일련의 과정을 이 설정파일이 담당하게 합니다.
(Spring 라이브러리를 추가 한 상황에서 file - new - other 들어가서 Spring을 찾으면 아래와 같이 뜹니다)
자. 그러면 이번엔 Spring을 사용해서
다시한번 도전해봅니다.
public interface ClassInter{
void show(String str);
}
public class ClassA implements ClassInter{
@Override
public void show(String str){
System.out.println("ClassA : " + str);
}
}
public class ClassB implements ClassInte{
@Override
public void show(String str){
System.out.println("ClassB : " + str);
}
}
public class WorkClass{
private ClassInter inter;
public void setInter(ClassInter inter) {
this.inter = inter;
}
public void showMain(){
inter.show("hello");
}
}
자. 이제 설정파일 인 init.xml을 구성해봅니다.
녹색 주석은 기존의 메인 클래스에서 객체를 생성하기 위해 사용했던 구문입니다.
이 설정파일에선 <bean>이 그 기능을 대신합니다.
<?xml version="1.0" encoding="UTF-8"?>
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">
<!-- ClassA classA = new ClassA() -->
<bean id="classA" class="simple_java.ClassA"/>
<!-- ClassB classB = new ClassB() -->a
<bean id="classB" class="simple_java.ClassB"/>
<!--
WorkClass workClass = new WorkClass()
workClass.setInter(classA);
-->
<bean id="workClass" class="simple_java.WorkClass">
<property name="inter" ref="classA"/>
</bean>
</beans>
이제 메인문을 보면
context로 init.xml파일을 가져오고
context를 통해 클래스를 생성하는게 아니라
사용할 클래스 workClass 를 가져옵니다.
public class WorkMain{
public static void main(String[] args){
//spring 사용
ApplicationContext context = new ClassPathXmlApplicationContext("init.xml");
//init.xml : 환경설정 파일
WorkClass work = (WorkClass)context.getBean("workClass"); //workClass 객체 가져오기
work.showMain();
}
}
Spring은 이렇게 기존 기능을 하는 클래스에서 객체를 생성하게 하지 않고
설정 파일인 xml파일에서 객체를 생성, 관리 하게 해서
객체간, 클래스간의 의존성을 줄이는 방법을 사용하게 합니다.
출처: https://sourcestudy.tistory.com/396 [study]
출처: https://sourcestudy.tistory.com/396 [study]