Whiteship's Note

[회사일] GenericService 버그 수정하기

프로젝트/SLT : 2010.06.09 17:45


앞에서 만들었던 GenericService에는 버그가 있었습니다. 저걸 적용한 뒤에 애플리케이션을 실행해보니까 제대로 동작하지 않더군요. 제길... 테스트를 만들껄.. 후회했습니다.  대부분의 코드가 단순 위임이라고 해서 테스트를 무시하면 안됩니다. 사실 Service 코드에서 하는 일은 DAO로 단순 위임하는 코드밖에 없어 보이지만 그 이상으로 복잡합니다.

그 중 하나가 DAO를 주입 받는 일이죠. '주입 하는 일'도 아니고 '주입 받는 일'을 무시했네요. 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/testContext.xml")
@Transactional
public class CodeServiceImplTest {

    @Autowired CodeService codeService;

    @Test
    public void di(){
        assertThat(codeService, is(notNullValue()));
    }

}

초간단 테스트를 만듭니다. codeService 자체를 DI 받을 수 있는지... 빈 팩토리에서 저 빈을 만들 수 있는지 확인합니다.

에러가 납니다.. ㅋ

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [osaf.dao.GenericDao] is defined: expected single matching bean but found 2: [codeDaoImpl, memberDaoImpl]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:779)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:686)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
... 43 common frames omitted

핵심적인 에러 메시지는 이 부분이다. 뭔 내용인지는 해설하지 않겠습니다. 퀴즈 삼아 맞춰보시죠.

public class GenericServiceImpl<D extends GenericDao<E, S>, E, S> implements GenericService<E, S> {

    @Autowired ApplicationContext applicationContext;
    private Class<D> daoClass;

    GenericDao<E, S> dao;

    public GenericServiceImpl(){
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        Type type = genericSuperclass.getActualTypeArguments()[0];
        if (type instanceof ParameterizedType) {
            this.daoClass = (Class<D>) ((ParameterizedType) type).getRawType();
        } else {
            this.daoClass = (Class<D>) type;
        }
    }

    public void add(E e) {
        dao.add(e);
    }

    public List<E> list(PageParam pageParam, S s) {
        pageParam.initCurrentPageInfosWith(dao.totalSize(s));
        return dao.list(pageParam, s);
    }

    public E getById(int id) {
        return dao.getById(id);
    }

    public void update(E e) {
        dao.update(e);
    }

    public void deleteBy(int id) {
        dao.deleteBy(id);
    }

    @PostConstruct
    public void setUpDao(){
        this.dao = this.applicationContext.getBean(daoClass);
    }
}

GenericService 구현체를 위와같이 수정했습니다. dao를 생성자에서 바로 연결하지 않고 @PostConstruct에서 연결한 것도 설명하지 않겠습니다. 요것도 심심하신 분 계시면 퀴즈 삼아 맞춰보시기 바랍니다.

퀴즈1. 에러가 난 원인은?
퀴즈2. 왜 생성자에서 applicationContext.getBean(daoClass)를 하면 안된느가?

top

  1. 심상호 2010.06.10 14:52 PERM. MOD/DEL REPLY

    1. attribute에 @Autowired가 걸려있는 경우 Class만 보고 Injection할 Type을 가져오는데 attribute는 GenericServiceImpl Class에 선언되어 있기 때문에 GenericDao Type을 Injection하려고 해서 에러가 발생한 것입니다.

    2. 생성자에서 할 경우 depends-on 설정이 없기 때문에 Injeciton할 Dao가 생성이 안되어 있을 수 있겠네요.

    그리고 기존의 GenericServiceImpl에서 Dao에 대한 set method를 생성하고 @Autowired를 attribute가 아닌 set method에 설정한 경우 정상적으로 동작합니다.

    Favicon of http://whiteship.me BlogIcon 기선 2010.06.10 16:25 PERM MOD/DEL

    1. 맞습니다. GenericDao 타입 빈을 주입하려다 보니 그 타입 빈이 두개 이상여서 그 중에 뭘 넣어야 할지 몰라서 에러가 났습니다.

    2. 안타깝지만 틀리셨습니다. depends-on이나 dao 객체보단 일단 생성자 내부에서는 applicationContext가 null이기 때문에 뭘 가져올수가 없습니다.

  2. 심상호 2010.06.10 16:36 PERM. MOD/DEL REPLY

    아. 그러네요. -_ -;;;

Write a comment.




: 1 : ··· : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 70 : 71 : ··· : 2638 :