목표
xml 대신 @Autowired 어노테이션을 사용하여 DI 하는 방법을 알아본다
기존 xml 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.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="exam" class="spring.di.entity.NewLecExam"/>
<bean id="console" class="spring.di.ui.GridExamConsole">
<property name="exam" ref="exam"/>
</bean>
<beans>
public class Program {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/setting.xml");
ExamConsole console = (ExamConsole) context.getBean("console");
console.print();
}
}
exam과 console이라는 이름을 가진 객체를 DI하고,
context 라는 IoC 컨테이너를 통해 해당 객체들을 가져와 사용한 모습이다.
어노테이션을 사용한 DI 방식
public class GridExamConsole implements ExamConsole {
private Exam exam;
...(생략)...
@Autowired
@Override
public void setExam(Exam exam) {
this.exam=exam;
}
}
<!-- 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"
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/util http://www.springframework.org/schema/util/spring-util-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="exam" class="spring.di.entity.NewLecExam"/>
<bean id="console" class="spring.di.ui.GridExamConsole"/>
<beans>
public class Program {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/setting.xml");
ExamConsole console = (ExamConsole) context.getBean("console");
console.print();
}
}
먼저 xml 파일에 context 네임스페이스를 추가한 뒤 <context:annotation-config> 태그를 추가한다.
그래야만 xml이 프로그램에서 bean 객체에 필요한 Dependency가 있을 경우, @Autowired 어노테이션들을 찾아본 뒤 알맞은 객체를 DI를 해주기 때문이다.
해당 태그를 추가하면 console 객체를 생성할 때, 내부 필드로 Exam 클래스가 있어 객체 정의가 필요하므로
추적 후 GridExamConsole 클래스의 @Autowired가 붙은 setter를 동작시키면서 먼저 만들어놓은 exam이라는 id의 Dependency를 Injection한다.
그런데 여기서 아래와 같이 같은 Exam 클래스 객체를 2개 이상 IoC 컨테이너에 생성했을 때는 어떤 일이 발생할까?
<!-- 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"
xmlns:p="http://www.springframework.org/schema/p"
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/util http://www.springframework.org/schema/util/spring-util-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="exam1" class="spring.di.entity.NewLecExam" p:kor="10" p:eng="20" p:math="30" p:com="40"/>
<bean id="exam2" class="spring.di.entity.NewLecExam" p:kor="15" p:eng="25" p:math="35" p:com="45"/>
<bean id="console" class="spring.di.ui.GridExamConsole"/>
<beans>
public class Program {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/setting.xml");
ExamConsole console = (ExamConsole) context.getBean("console");
console.print();
}
}
console이라는 id를 가진 객체가 생성될 때 exam이라는 dependency를 setter에 주입해줘야 하는데,
같은 클래스 객체가 dependency로 2개가 생성되어 있어 어떤 것을 주입해야할지 몰라 에러가 발생한다.
이런 문제를 해결하기 위해 나온 어노테이션이 @Qualifier다.
(exam의 id와는 상관없이 Qualifier해주지 않은 상태라면, 스프링은 클래스를 기준으로 DI를 시도한다.)
public class GridExamConsole implements ExamConsole {
private Exam exam;
...(생략)...
@Autowired
@Qualifier("exam1")
@Override
public void setExam(Exam exam) {
this.exam=exam;
}
}
위와 같은 방법으로 setter에 Qualifier를 통해 특정 id를 명시하면, 해당 Dependency를 주입하면서 같은 클래스의 Dependency가 여러개 있어도 에러가 발생하지 않는다.
@Autowired가 위치할 수 있는 자리
1) setter 앞
위에서 사용했으니 예시 생략
2) 생성자 매개변수 앞
public class GridExamConsole implements ExamConsole {
private Exam exam;
private Exam exam2;
@Autowired
public GridExamConsole(@Qualifier("exam1")Exam exam,@Qualifier("exam2")Exam exam2){
this.exam=exam;
this.exam2=exam2;
}
...(생략)...
@Autowired
@Qualifier("exam1")
@Override
public void setExam(Exam exam) {
this.exam=exam;
}
}
생성자에 @Autowired를 사용할 때는 setter와 달리 매개변수 앞에 사용한다.
이유는 위 코드에서처럼 매개변수에 2개 이상의 같은 Dependency가 주입되어야 할 수 있기 떄문이다.
이런 혼동을 사전에 방지하기 위해 매개변수 앞에만 사용할 수 있도록 설계되었다.
3) Dependency로 사용할 필드 앞
public class GridExamConsole implements ExamConsole {
@Autowired
@Qualifier("exam1")
private Exam exam;
...(생략)...
@Override
public void setExam(Exam exam) {
this.exam=exam;
}
}
필드 앞에 @Autowired를 사용하면, 기본 생성자를 통해 Dependency가 생성된다.
@Autowired(required=false)
public class GridExamConsole implements ExamConsole {
@Autowired(required=false)
@Qualifier("exam1")
private Exam exam;
...(생략)...
@Override
public void print(){
if(exam==null)
System.out.println("Dependency 생성 실패해서 null임");
else
System.out.println("Dependency 생성 성공하였음");
}
}
required=false 옵션을 사용하면, 만약 Dependency 생성에 실패한 경우 null 값을 반환한다.
이를 위처럼 활용할 수도 있게 된다.
'뉴렉쳐 스프링 프레임워크 정리 > Part1. DI' 카테고리의 다른 글
[Spring 개념정리] Java를 통한 Configuration (0) | 2023.07.14 |
---|---|
[Spring 개념정리] 어노테이션을 이용한 객체생성 (0) | 2023.07.14 |
[Spring 개념정리] xml을 사용한 DI 및 IoC 컨테이너 사용법2 (0) | 2023.07.14 |
[Spring 개념정리] xml을 사용한 DI 및 IoC 컨테이너 사용법 (0) | 2023.07.14 |
[Spring 개념정리] DI와 IoC Container (0) | 2023.07.14 |