my study.

Study/Spring

[Spring] Spring Boot NoUniqueBeanDefinitionException 문제

fftl 2022. 1. 30. 16:20

문제 발생

Github에서 다른 분들이 작업한 프로젝트를 보며 공부를 하던 중 Service를 interface로 만든 뒤 구현을 ServiceImpl로 만든 뒤 사용하는 것을 보게 되었습니다. 저는 Service를 바로 클래스로 구현을 해서 사용하는 중이기 때문에 왜 Service를 interface로 만들어서 사용하는지 궁금했습니다.

 

그래서 찾아보니 이는 예전부터 Spring을 사용해오던 사람들과 또는 그로부터 Spring을 배우게 된 분들이 주로 사용하게 되는데 예전에 Spring은 proxy를 생성할 때에 interface 타입만을 사용하여 proxy를 생성할 수 있었고 이를 주입받아서 사용해야 했습니다. 하지만 요즘은 class 타입을 이용하여도 proxy를 생성할 수 있고 이를 이용해서 주입받아서 사용할 수 있습니다. 하지만 관습처럼 사용해 오던 interface를 이용한 Service 생성 방식 계속해서 사용하는 것으로 볼 수 있습니다. ( 물론 interface를 이용한 Service생성 방식은 코드를 유연하게 작성할 수 있다는 이점이 있다고 생각합니다. )

 

위의 내용은 application.properties에서 아래와 같이 설정을 할 수 있습니다. 만약 기본값인 true로 놓아둔다면 class 타입을 이용한 proxy 타입을 사용할 수 있기 때문에 interface를 사용하지 않아도 되지만 만약 임의로 false로 설정을 한다면 interface를 이용해 Service를 만든 뒤 ServiceImpl로 구현을 하고 사용을 해야되게 됩니다.

spring.aop.proxy-target-class=false;

spring.aop.proxy-target-class=true; //default

 

아무튼 위의 내용을 확인하고 만약 interface 사용하는 상황에서의 코드를 살펴보다 보니 아래와 같은 상황이 궁금해졌습니다.

 

//MyTest.java

package fftl.lookupBoard.service;

public interface MyTest {
    Integer MyCalculate(int a, int b);
}

 

//MyTestT1

package fftl.lookupBoard.service;

import org.springframework.stereotype.Service;

@Service
public class MyTestT1 implements MyTest{

    @Override
    public Integer MyCalculate(int a, int b) {
        return a*10 + b*10;
    }
}

//MyTestT2

package fftl.lookupBoard.service;

import org.springframework.stereotype.Service;

@Service
public class MyTestT2 implements MyTest{

    @Override
    public Integer MyCalculate(int a, int b){
        return a+b;
    }
}

위와 같은 MyTest가 존재하고 이를 기반으로 만든 두 개의 MyTestT1, MyTestT2가 존재한다면 MyTest의 MyCalculate를 사용할 때 어떤 MyCalculate가 실행이 되는지 궁금했습니다. 그래서 만들고 있던 프로젝트에서 잠깐 테스트를 진행해 보았습니다.

 

 

실행시켰더니 아래와 같이 NoUniqueBeanDefinitionException 과 함께 하나의 Bean과 매칭이 가능한데 두 개가 발견되었다는 문제가 나타났습니다.

이에 대한 해결 방법을 찾아보았고 몇가지의 방법이 있다는 것을 알게 되었습니다.

 

1. 의존성을 주입 할 때에 구현 클래스인 MyTestT1 또는 MyTestT2를 직접 주입하여 사용하는 것 입니다.

2. 의존성을 주입 할 때에 MyTest 인터페이스를 주입 하되 필드의 이름을 bean 이름인 myTestT1, 또는 myTestT2를 사용하면 해당 클래스를 주입받을 수 있습니다.

3. @Qualifier 또는 @Primary 어노테이션을 사용하여 우선권을 줍니다.

 

자세한 설명은 출처 링크에 가시면 자세하게 확인하실 수 있습니다. 아무튼 Spring 에서 어떠한 기준으로 두가지 이상의 Bean을 가진 인터페이스에는 어떻게 대응을 하는지 궁금했고 방법에 대해서 생각해보는 시간을 가지게 되었습니다. 이상입니다.

 

 

출처

https://prodo-developer.tistory.com/121