Field testService in com.example.TestController required a single bean, but 3 were found:
- testServiceImpl1: defined in file [build/classes/java/main/com/example/TestServiceImpl1.class]
- testServiceImpl2: defined in file [build/classes/java/main/com/example/TestServiceImpl2.class]
- testServiceImpl3: defined in file [build/classes/java/main/com/example/TestServiceImpl3.class]
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
@Primary, @Qualifier
이런 문제에서 생기는 스프링 에러다.
빈이 여러 개인 경우 생기는 문제인데
밑이 보면 해결 방법으로 3가지를 제시해 준다.
- Consider marking one of the beans as @Primary
- updating the consumer to accept multiple beans
- using @Qualifier to identify the bean that should be consumed
예제 코드로 확인해보자
TestController
@RequiredArgsConstructor
@RestController
public class TestController {
private final TestService testService;
@GetMapping("")
public String test() {
return String.valueOf(testService.test());
}
}
TestService
public interface TestService {
int test();
}
@Service
public class TestServiceImpl1 implements TestService {
@Override
public int test() {
return 1;
}
}
@Service
public class TestServiceImpl2 implements TestService {
@Override
public int test() {
return 2;
}
}
@Service
public class TetServiceImpl3 implements TestService {
@Override
public int test() {
return 3;
}
}
이렇게 돼있을 때 실행 시 게시글 상단의 에러를 만날 수 있다.
@Primary
그럼 첫 번째 방법을 사용해 본다.
@Primary
@Service
public class TestServiceImpl1 implements TestService {
@Override
public int test() {
return 1;
}
}
내가 원하는 클래스에 @Primary를 붙여주고 확인하면 그 클래스로 실행이 된다.
나는 TestServiceImpl1에 붙여줬으니 1이 나온다.
List
다음으로 두 번째 방법은 List로 만들어 사용하는 것이다.
@RequiredArgsConstructor
@RestController
public class TestController {
private final List<TestService> testServiceList;
@GetMapping("")
public String test() {
return String.valueOf(testServiceList.get(1).test());
}
}
이렇게 하게 되면 3개의 클래스 모두 사용 가능한데 여기서 get()으로 인덱스 값을 넣어서 가져오면 된다.
리스트에 들어가는 순서는 클래스 이름순이다.
따로 명시하지 않아도 TetServiceImpl1 -> tetServiceImpl1, TetServiceImpl2 -> tetServiceImpl2 이렇게 된다.
@Qualifier
마지막으로 세 번째 방법으로는 @Qualifier를 사용하는 것이다.
세 번째 방법에는 여러 가지의 방법이 있다.
@RestController
public class TestController {
@Autowired
@Qualifier("testServiceImpl3")
private TestService testService;
@GetMapping("")
public String test() {
return String.valueOf(testService.test());
}
}
위의 코드처럼 @Autowired를 사용할 경우 @Qualifier("") 명시해 주면 된다.
생성자를 통한 주입의 경우에는 밑에처럼 사용하면 된다.
@RestController
public class TestController {
private final TestService testService;
public TestController(@Qualifier("testServiceImpl3") TestService testService) {
this.testService = testService;
}
@GetMapping("")
public String test() {
return String.valueOf(testService.test());
}
}
마지막으로 Lombok의 @RequiredArgsConstructor와 같이 사용할 경우에는 문제가 있지만 이 방법으로 사용하기 위해서는
프로젝트의 루트 폴더에 밑에 lombok.config
파일을 만들어주고 아래의 내용을 적어주면 된다.
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
작성이 끝났다면 RequiredArgsConstructor와 함께 사용해 보자
@RequiredArgsConstructor
@RestController
public class TestController {
@Qualifier("testServiceImpl3")
private final TestService testService;
@GetMapping("")
public String test() {
return String.valueOf(testService.test());
}
}
이렇게 해주면 고민이 해결된다.
마지막으로 기본으로 설정되는 이름 말고 본인이 원하는 이름을 사용하고 싶은 경우에는 아래와 같이 사용하자
@Qualifier("test1")
@Service
public class TestServiceImpl1 implements TestService {
@Override
public int test() {
return 1;
}
}
@Qualifier("test2")
@Service
public class TestServiceImpl2 implements TestService {
@Override
public int test() {
return 2;
}
}
마지막 @RequiredArgsConstructor & @Qualifier의 참조 링크
SPRING + LOMBOK + @QUALIFIER OR INJECTION JUST BECAME A BIT EASIER (PART 2 OF 2)
- 끗 -