나는 같은 인터페이스를 구현한 여러 클래스를 하나의 클래스에 등록을 해서 순차적으로 실행을 하는 프로그램을 만들었다.
특정 기능에서 후처리하는 것을 같은 인터페이스를 구현해서 일괄적인 처리를 하는 것과 동시에 어떤 일을 하는지 명확히 알수가 있어서 좋았다. 여러가지 작업들이 있었지만, 모두 런타임시에 에러가 발생을 하면 무시를 하고 넘어가야 할 작업들이었다.
버그의 원인으로 의심을 한것
1. 외부서비스와 연동을 통해서 값을 얻어오는데, 연결이 잘 안되거나, timeout이 발생한다.
2. 데이터를 DTO로 populate를 하는데, null인 값을 호출을 한다.
3. spring configuration이 문제가 있다.
사실 3번을 로그를 확인하다가 가장 나중에 알아냈다. 같은 작업을 2번을 등록했지만, 뒤에 실행되는 것 하나는 런타임에 에러가 나서 이클립스 디버깅 모드로 돌려도 한번 밖에 실행이 되지 않을 것이었다. 물론, 예외처리를 무시하게 했던 글로벌 에러처리를 제거했으면 빠른 시간에 해결이 되었겠지만, 너무 많은 로그들이 나오고 있어서 눈으로 학인하기가 쉽지 않은 상태였다.
같은 인터페스의 구현체를 2번이나 등록을 하는 실수를 범했다. 내가 눈으로 확인했을 당시에는 등록하는 클래스의 ref에는 이름이 중복된게 없었다.
아래와 같이 한 것이다. 똑같은 구현체를 다른 이름으로 2번을 등록했다.
<bean id="movieLister1" class="x.y.SimpleMovieLister" />
<bean id="movieLister2" class="x.y.SimpleMovieLister" />
<bean id="tasksAfter" class="x.y.TasksAfter" >
<property name="afterTasks">
<list>
<ref bean="movieLister1" />
<ref bean="movieLister2" />
</list>
</property>
</bean>
<bean id="movieListerMain" class="x.y.SimpleMovieListerMain">
<property name="tasksAfter" ref="tasksAfter" />
</bean>
<bean id="movieLister2" class="x.y.SimpleMovieLister" />
<bean id="tasksAfter" class="x.y.TasksAfter" >
<property name="afterTasks">
<list>
<ref bean="movieLister1" />
<ref bean="movieLister2" />
</list>
</property>
</bean>
<bean id="movieListerMain" class="x.y.SimpleMovieListerMain">
<property name="tasksAfter" ref="tasksAfter" />
</bean>
물론, 처음부터 잘 등록을 하면 되었으나, 간혹 C&P를 하다가 실수를 할수도 있고, configuration이 길어지게 되면, 눈으로 보는 것도 집중력이 떨어진다. (사람의 눈을 얼만큼 믿어야 할까?)
spring context가 올라갈때 에러가 발생을 하지 않으니 configuraiton에 의심도 하지 않았다.
계속된 NullPointerException이 나와도 과거에 외부 서비스가 연동이 안되면 Null이 떨어진 경험이 나를 더욱더 잘못된 곳에서 디버깅을 하게 만들었다. 한정된 경험은 시야를 무척이나 좁게 만든다.
같이 디버깅을 하던 자바지기가 하나의 메일을 주었다. 이것은 그 동안에 묵혔던 버그를 해결하게 충분한 기능이었다. 나에게는 운이 좋았다...^^
아래 내용으로 해결이 어려울 수도 있지만, spring context에 의도하지않은 객체 생성에 대해서 그리고, 의도한 dependency를 위해서 유용한 방법이다.
내용은 다음과 같다.
http://www.springframework.org/docs/reference/metadata.html
http://www.springframework.org/docs/api/org/springframework/beans/factory/annotation/Required.html
정리를 해보면, 아래와 같이 설정을 하면된다.
1. required사용을 위해서 빈을 등록한다.
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
2. 사용할 빈을 등록한다.
<bean id="movieLister" class="x.y.SimpleMovieLister">
<!-- whoops, no MovieFinder is set (and this property is @Required) -->
</bean>
<!-- whoops, no MovieFinder is set (and this property is @Required) -->
</bean>
3. 코드상에 setter에 @Required을 넣어준다.
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can 'inject' a MovieFinder
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually 'uses' the injected MovieFinder is omitted...
}
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can 'inject' a MovieFinder
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually 'uses' the injected MovieFinder is omitted...
}
4. 제대로 DI가 되지 않으면 context 로딩시 다음과 같이 예외를 발생한다.
Exception in thread "main" java.lang.IllegalArgumentException:
Property 'movieFinder' is required for bean 'movieLister'.
Property 'movieFinder' is required for bean 'movieLister'.
좀더 optional한 사용법은 여기를 참조
http://younghoe.info/trackback/504
트랙백 보낼 주소 :: http://www.ologist.co.kr/trackback/701
-
Constructor Injection vs Setter Injection
from ologist`s blog2.02007/07/18 09:43Constructor Injection, Setter Injection에 대해서 이야기를 해보자.우리는 spring에서 DI를 할때 대부분 setter injection을 선호하거나 습관적으로 사용을 한다.spring개발자들이 setter를 더 선호하기도 하고, 객체생성의 편리성을 위해서 가능한 setter를 많이 사용을 하고 있다. 그리고, 생성자에 dependency가 늘어가면 클래스가 보기 싫은 경우도 해당이 될 것이다. 2~4개정도는 괜찮아..

