이번 시간에는 어노테이션을 이용해서 코드 파일에 초기화 설정을 심는 방법에 대해 알아보고자 한다.
그럼 xml 파일의 코드를 하나씩 어노테이션으로 변경하는 작업을 수행해보자.
우선 다음 코드를 변경해보자
<property name="exam" ref="exam"/>
해당 코드를 주석처리 또는 삭제한 뒤, 실제 클래스로 들어가 setter 함수에 어노테이션을 심어준다.
@Autowired
@Override
public void setExam(Exam exam) {
this.exam = exam;
}
위처럼 하면 객체가 만들어질 때, 해당 객체 안에 있는 @Autowired 어노테이션이 포함된 setter를 자동으로 찾게 된다. 즉, 객체가 자동으로 연결되어 실질적으로 앞에서 보았던 xml 코드의 역할을 대신하게 되는 것이다.
다만 이 상태에서 프로그램을 실행하면 오류가 발생한다. 그 이유는 스프링은 특별한 지시가 없는 한 굳이 해당 클래스를 뒤져가며 setter를 찾지 않는다. 생성하는 객체 안을 굳이 다 조사해보지 않는다는 것이다. 따라서 스프링에게 따로 지시를 해주어야 하는데, 그 방법은 다음과 같다.
우선 Namespaces 탭으로 이동하여 context를 체크해준다.
이후 Source 탭으로 돌아가 아래의 코드를 객체 생성 코드 위에 추가해준다.
<context:annotation-config />
위 코드의 의미는 아래의 객체들이 어노테이션을 갖고 있다는 것을 스프링에게 알려주는 것이다. 이렇게 하면 객체를 만들면서 해당 객체 안에 있는 설정이 xml에서 빠져있는 것이 있는지 확인하게 된다.
이후 실행해보면 정상적으로 작동되는 것을 확인할 수 있다.
그렇다면 @Autowired는 무슨 근거를 가지고 객체를 연결하는 것일까? 이것은 xml 코드를 살짝 수정함으로써 알아낼 수 있다.
xml 코드를 다음과 같이 수정해보았다.
<context:annotation-config />
<!-- Exam exam = new NewlecExam(); -->
<bean id="exam1" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="20" p:math="40" p:com="30"/>
<!-- <bean id="exam" class="spring.di.entity.NewlecExam">
<constructor-arg name="kor" value = "10" />
<constructor-arg name="eng" value = "20" />
<constructor-arg name="com" value = "30" />
<constructor-arg name="math" value = "40" />
</bean> -->
객체의 이름만 exam에서 exam1로 변경하였으며, 실행해보면 오류 없이 정상적으로 실행되는 것을 볼 수 있다. 심지어는 id 속성을 아예 지워버려도 상관이 없다.
따라서 여기서 알 수 있는 것은 객체의 이름이 아닌 자료형에 따라 객체를 연결한다는 것이다. 그런데 현재 setter에서 받아오는 자료형은 인터페이스이고 xml 코드상에서 생성한 객체의 자료형은 그것을 구현한 클래스이다. 즉, 해당 인터페이스를 참조할 수 있는 객체를 자동으로 찾아와주고 바인딩 해준다는 것이다.
그렇다면 같은 클래스의 두 객체가 이름이 정해져 있지 않고 속성 값만 다르다면 @Autowired가 잘 작동할까? 당연히 오류가 발생하게 된다. 이럴 경우 사용할 수 있는 방법이 xml 코드의 id 속성을 지정하는 것이다. 이때 같이 사용되는 @Qualifier 어노테이션이 있는데, 다음과 같이 사용할 수 있다.
@Autowired
@Qualifier("exam1")
@Override
public void setExam(Exam exam) {
this.exam = exam;
}
즉, xml 코드에서 id 속성이 exam1인 객체를 찾아서 바인딩한다는 의미이다.
@Autowired 어노테이션은 위치에 따라 그것의 용도가 달라지게 된다. 위에서는 setter 함수 위에 작성되었기 때문에 해당 setter 함수를 호출하면서 Injection하는 것이다.
만약 @Autowired를 다음과 같이 속성 위에 작성하게 되면 기본 생성자를 호출하게 된다.
@Autowired
@Qualifier("exam1")
private Exam exam;
만약 기본 생성자가 없고 일반 생성자만 있다면 오류가 발생하게 된다. 이럴 경우 기본 생성자를 직접 생성하거나 일반 생성자를 지우는 방식으로 해결할 수 있다.
@Autowired를 일반 생성자 위에 작성한다면 어떻게 될까? 이럴 경우 @Qualifier 어노테이션 부분에서 오류가 발생하게 되는데, 그 이유는 setter 함수는 하나의 객체만 받아오지만 일반 생성자의 경우 객체를 2개 이상 받아올 수 있기 때문이다.
따라서 이럴 경우 다음과 같이 각자 지정할 수 있게끔 매개변수에서 설정할 수 있다.
@Autowired
public InlineExamConsole(@Qualifier("exam1")Exam exam1, @Qualifier("exam2")Exam exam2) {
this.exam = exam1;
}
하지만 지금은 객체를 하나만 사용할 것이므로 그냥 @Qualifier 어노테이션을 지워주었다.
@Autowired
public InlineExamConsole(Exam exam) {
this.exam = exam;
}
@Autowired 어노테이션의 적절한 위치는 어디까지나 개인의 취향에 따라 나뉘게 된다. setter 함수 위에 작성하든, 속성 위에 작성하든 그때그때 상황에 따라 바뀔 수 있다는 것이다.
추가로 만일 @Autowired로 인해 바인딩 된 객체가 없을 경우 오류가 발생하게 될텐데, 분기를 만들어 이러한 오류를 방지하고 싶다면 다음과 같이 설정할 수 있다.
@Autowired(required = false)
@Qualifier("exam1")
private Exam exam;
@Override
public void print() {
if(exam == null)
System.out.printf("total is %d, avg is %f", 0, 0.0);
else
System.out.printf("total is %d, avg is %f", exam.total(), exam.avg());
}
이렇게 하면 exam이 null일 경우에도 오류 메시지가 발생하는 대신 다음과 같이 결과가 출력된다.
'🍃 Spring, Spring Boot > 스프링 프레임워크 기초' 카테고리의 다른 글
[Java / Spring] 6. XML Configuration을 Java Configuration으로 변경하기 (0) | 2022.04.11 |
---|---|
[Java / Spring] 5. 어노테이션을 이용한 객체 생성 (0) | 2022.04.11 |
[Java / Spring] 3. 콜렉션 생성과 목록 DI (0) | 2022.04.11 |
[Java / Spring] 2. DI 값 설정 (0) | 2022.04.11 |
[JAVA / Spring] 1. 스프링을 통해 DI 설정 (0) | 2022.04.10 |