package spring.di;
import spring.di.entity.Exam;
public class Program {
public static void main(String[] args){
Exam exam = new NewlecExam();
//ExamConsole console = new InlineExamConsole(exam);
ExamConsole console = new GridExamConsole(exam);
console.print();
}
}
위의 코드에는 하나는 Exam 객체, 하나는 ExamConsole 객체 총 두개의 객체가 있다. 크게 보면 ExamConsole 객체가 NewlecExam 객체를 사용하고 있는데 이렇게 결합하는 방식은 위의 코드처럼 생성자를 통한 방식과 setter를 통한 방식 두 가지 방식이 존재한다.
package spring.di;
import spring.di.entity.Exam;
public class Program {
public static void main(String[] args){
Exam exam = new NewlecExam();
//ExamConsole console = new InlineExamConsole(exam);
//ExamConsole console = new GridExamConsole(exam);
ExamConsole console = new GridExamConsole(); // 생성자 수정
console.setExam(exam); // setter 추가
console.print();
}
}
위의 코드같은 경우는 setter를 통해서 결합하는 방법이다. 일반적인 방식으로는 이렇게 setter를 통해서 exam을 넘겨받는 이러한 결합방법이다. (GridExamConsole이 setter를 갖고 있는 것이다.)
이 setter를 통해서 injection 하는 것을 가능하게 하기 위해서 setter메서드를 아래와 같이 구현해준다.
이제 setter 메서드를 완성해주었으니 아래 그림과 같이 기본 생성자도 생성해주자
(ctrl+space를 누르면 기본 생성자를 자동으로 추가할 수 있다.)
이제 인젝션이 생성자를 통하는 방법과 세타를 통하는 방법 두 가지 모두 가능하다. 이 두 가지 방법을 스프링을 통해서도 설정하는 방법을 알아보자.
package spring.di;
import spring.di.entity.Exam;
public class Program {
public static void main(String[] args){
Exam exam = new NewlecExam();
ExamConsole console = new GridExamConsole();
console.setExam(exam);
console.print();
}
}
만약 NewlecExam 객체가 바뀐다거나 GridExamConsole이 바뀌는 상황이 생길 수 있다. 그래서 변경되는 객체에 대해서는 설정으로 빼는 게 바람직하다라는것이 스프링의 기능이다.
또한 위의 두 객체만 바뀌면 되는 것이 아니라 아래의 결합관계도 바뀌어야 한다.
console.setExam(exam);
결국 아래의 주석으로 처리된 코드들을 각각 다 분리해서 설정으로 뺴주어야한다.
즉, 주석으로 처리되있는 부분을 우리가 설정으로 빼게 되면 설정으로 뺀 녀석을 객체화해서 나에게 전달해주는것이 바로 스프링이다. 스프링의 기능을 이용해서 아래의 코드에서 주석 처리 된 부분과 주석이 되지 않은 부분 이 둘 사이의 관계를 분리해야한다.
/* 설정으로 뺌
Exam exam = new NewlecExam();
ExamConsole console = new GridExamConsole();
console.setExam(exam);
*/
ExamConsole console = ?; // 이걸 어떻게 할 것인가?
console.print();
지난 시간에 spring.di 패키지에 setting.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >
Exam exam = new NewlecExam();
</beans>
지시서에 Exam exam = new NewlecExam()처럼 자바 코드로 사용하면 좋겠지만 지시서이기 때문에 데이터 기반으로 작성해야한다. 그렇다면 어떻게 이걸 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >
// Exam exam = new NewlecExam();
<bean id="" class="" />
</beans>
스프링은 bean 태그를 이용해서 생성할 객체를 지시해줘야하는데 id와 class를 채워줘야한다.
// <!--Exam exam = new NewlecExam(); -->
<bean id="exam" class="spring.di.entity.NewlecExam" />
어떠한 클래스를 객체화할 것인지를 class에 작성하고 그 객체를 어떠한 이름으로 사용할것인지 즉, 변수명을 id에 작성해주면된다.
주의사항으로는 class에 같은 이름의 클래스를 갖고 있는 다른 객체가 있을 수 있으니 반드시 패키지명까지 같이 써줘야한다.
<?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-3.0.xsd" >
// <!--Exam exam = new NewlecExam(); -->
<bean id="exam" class="spring.di.entity.NewlecExam" />
// <!-- ExamConsole console = new GridExamConsole(); -->
<bean id="console" class="spring.di.ui.GridExamConsole" />
</beans>
같은 방법으로 ExamConsole console = new GridExamConsole(); 코드도 동일한 방법으로 작성해준다.
console.setExam(exam);
그러면 console.setExam(exam)은 어떻게 할까?
// <!-- ExamConsole console = new GridExamConsole(); -->
// <bean id="console" class="spring.di.ui.GridExamConsole" />
<bean id="console" class="spring.di.ui.GridExamConsole" >
</bean>
지금 console 이라는 녀석이 exam이라는 녀석을 갖게 되는 구조라서 ExamConsole console = new GridExamConsole()를 xml 형태로 작성한 코드에서 <bean/>과 같이 단일 태그로 닫은것을 <bean></bean>로 열고 닫아준다.
// <!-- ExamConsole console = new GridExamConsole(); -->
// <bean id="console" class="spring.di.ui.GridExamConsole" />
<bean id="console" class="spring.di.ui.GridExamConsole" >
<property name="setExam"></property>
</bean>
그 후 bean 태그 사이에 property 태그가 존재하는데 name 속성에 setExam 메서드를 적어준다.
// <!-- ExamConsole console = new GridExamConsole(); -->
<bean id="console" class="spring.di.ui.GridExamConsole" >
// <!-- console.setExam(exam); -->
<property name="exam"></property>
</bean>
Spring은 XML 설정 파일에서 property 태그의 name 속성을 보고 해당 필드에 접근할 수 있는 setter 메서드를 자동으로 찾는데 setExam 메서드의 이름에서 set을 제외한 exam이 필드 이름으로 간주된다. 따라서 <property name="exam">로 set을 생략하고 대문자를 소문자로 작성해야 Spring이 setExam 메서드를 찾을 수 있다.
// <!--Exam exam = new NewlecExam(); -->
<bean id="exam" class="spring.di.entity.NewlecExam" />
// <!-- ExamConsole console = new GridExamConsole(); -->
<bean id="console" class="spring.di.ui.GridExamConsole" >
// <!-- console.setExam(exam); -->
<property name="exam" value="exam?" ref="exam?" /> // value or ref
</bean>
그리고 console.setExam(exam)에서 여기에 설정하는 객체의 이름 exam을 value속성 혹은 ref속성에 넣어주면되는데 value에 들어갈 수도 있고 ref(레퍼런스)에 들어갈 수도 있다. exam 이것의 타입이 value 타입이면 value에 작성하고 레퍼런스 타입이면 ref에 작성하면 된다.
// <!--Exam exam = new NewlecExam(); -->
<bean id="exam" class="spring.di.entity.NewlecExam" />
// <!-- ExamConsole console = new GridExamConsole(); -->
<bean id="console" class="spring.di.ui.GridExamConsole" >
// <!-- console.setExam(exam); -->
<property name="exam" ref="exam" />
</bean>
spring.di.entity.NewlecExam은 레퍼런스 타입이다. 그래서 value가 아닌 ref에 작성하면된다.
정리하자면 spring.di.ui.GridExamConsole 객체를 console이라는 변수명으로 사용하는것이고 spring.di.ui.GridExamConsole 객체가 setExam 이라는 setter 메서드를 갖고 있어야 <property name="exam" />와 같이 사용할 수 있다.
package spring.di.ui;
import spring.di.entity.Exam;
public class GridExamConsole implements ExamConsole {
private Exam exam; // 속성을 물어보는게 아님
public GridExamConsole(Exam exam) {
this.exam = exam;
}
@Override
public void print() {
System.out.println("┌─────────┬─────────┐");
System.out.println("│ total │ avg │");
System.out.println("├─────────┼─────────┤");
System.out.printf("│ %3d │ %3.2f │\n",exam.total(),exam.avg());
System.out.println("└─────────┴─────────┘");
}
// 없으면 안된다.
@Override
public void setExam(Exam exam) {
this.exam = exam;
}
}
또한 <property name="exam" ref="exam" />에서 name="exam"은 exam 속성을 말하는것이 아닌 setExam 메서드를 뜻하는것에 주의하자
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
우리가 이렇게 지시 사항을 만들었으면 지시서를 이용해서 실제 객체화하는 작업은 ApplicationContext라고 하는 것을 이용해서 지시사항을 전달하는데 다음 시간에 ApplicationContext를 통해 생성해 보도록 하자.
참고자료
'🖥️ Backend > Spring' 카테고리의 다른 글
[Spring] 9.값 형식 DI (1) | 2024.11.08 |
---|---|
[Spring] 8.스프링 IoC 컨테이너 사용하기(ApplicationContext 이용하기) (1) | 2024.11.06 |
[Spring] 6.스프링 DI 설정을 위해 이클립스 플러그인 설치하기 (3) | 2024.10.20 |
[Spring] 5.Spring없이 Dependency를 직접 Injection하기 (0) | 2024.10.08 |
[Spring] 4.IoC(Inversion Of Control) 컨테이너 (1) | 2024.10.02 |