@Qualifier 무시 오류(Lombok @RequiredArgsConstructor)
라이브러리 개발도중 발생한 문제이다.
우선 Bean 과 의존성 주입에 대해서 간략하게 살펴보도록 하자.
Spring IoC Container 는 Bean 을 생성,관리 하여 의존성 주입을 대신 해준다.
다만 Bean 으로 등록하게 될 타입이 여러개가 존재한다면 개발자가 어떤 Type 을 사용할 것인지 명시해 주어야 한다.
// Interface
public interface ServiceTarget{}
// Impl 1
@Service
public class TargetImpl implements ServiceTarget {}
// Impl 2
@Service
public class TargetImpl2 implements ServiceTarget {}
// use
@RestController
public class Controller {
@Autowired
private final ServiceTarget service;
}
위의 예시 코드는 어떤 Bean 을 명시해 주지 않은 예제 코드이다.
이경우 IDE 에서부터 Warn 을 하게 된다. interface 의 구현체가 2개인데 어떤것을 사용하여 Bean 생성, 주입을 할지 IoC Container 에서는 모르기 때문이다. 이럴경우 Bean 을 구분하는 방법은 아래와 같다.
- Field 명 Mathcing
- @Primary
- @Qualifier
1. Field 명 매칭
// use
@RestController
public class Controller {
@Autowired
private final ServiceTarget targetImpl;
}
Spring 의 경우 동일 타입의 Bean 이 여러개라면, 이름으로 한번더 조회하게 된다.
이 경우 interface 의 어떤 구현체를 사용할것인지 해당 구현체의 이름으로 적용하여 사용 할수있다.
2. @Primary
// Interface
public interface ServiceTarget{}
// Impl 1
@Service
@Primary
public class TargetImpl implements ServiceTarget {}
// Impl 2
@Service
public class TargetImpl2 implements ServiceTarget {}
// use
@RestController
public class Controller {
@Autowired
private final ServiceTarget service;
}
해당 어노테이션은 동일 타입 Bean 이 여러개일때 우선순위를 지정해준다.
동일 타입 Bean 중 가장 우선권을 가지며, 별다른 설정이 되어있지 않을때 Bean 으로 등록이 된다.
3. @Qualifier
// Interface
public interface ServiceTarget{}
// Impl 1
@Service
@Primary
public class TargetImpl implements ServiceTarget {}
// Impl 2
@Service
public class TargetImpl2 implements ServiceTarget {}
// use
@RestController
public class Controller {
@Qualifier("targetImpl2")
@Autowired
private final ServiceTarget service;
}
해당 어노테이션은 여러개의 타입중 어떤것을 구분하여 사용할것인지 지정해주는 역할을 한다.
하지만 위의 코드 처럼 @Primary 와 @Qualifier 가 동시에 존재하는 경우
@Qualifier 의 타입이 우선권을 가지게 된다.
Spring 에서 @Primary, @Qualifier 중 Bean 의 우선권을 지정하는 코드는
org.springframework.beans.factory.support.DefaultListableBeanFactory.determineAutowireCandidate() 내에 존재한다.
문제 상황
대략적으로 Bean 의 주입과 우선권과 같은 기본 지식을 알아봤으니 실질적 문제상황으로 가보도록 하겠다.
public interface SampleRepository {
void save(String msg);
String get(String msg);
}
위와 같은 Interface 가 존재하고, 구현체는 2개가 존재하며, @Primary 가 붙은 Class, 붙지않은 Class 가 존재한다.
(@Qualifier 의 샘플 코드와 구조가 동일하다.)
@Qualifier 어노테이션을 사용 하였으니 당연히 Log 에는 'SECOND ~' 관련 해서 남아야 하는데 'FIRST ~' 관련 log 만 남았다.
DipController.class 를 열어보면 아래와 같이 @Qualifier 가 빠져있는걸 확인할 수 있다.
이러한 문제는 Lombok 과의 충돌로 발생하였으며
https://github.com/projectlombok/lombok/issues/745 의 티켓에서 확인 할 수 있었다.
롬복의 @RequiredArgsConstructor 은 어노테이션을 포함하여 Constructor 를 만들지 않기 때문에 발생한 문제였다.
1. 생성자 주입 방식 해결
Lombok 을 사용하지 않고 전통적인 방식으로 사용한다면 당연히 Qualifier 의 타입이 사용된다.
@RestController
@Slf4j
@RequestMapping("/api/v1/dip")
public class DipController {
@Qualifier("sampleRepositoryImpl_second")
private final SampleRepository sampleRepository;
public DipController(@Qualifier("sampleRepositoryImpl_second") SampleRepository sampleRepository) {
this.sampleRepository = sampleRepository;
}
2. Lombok 과 함께 사용하는 방법
/src/main/java/lombok.config 파일을 추가하여 아래와 같은 코드를 추가해주면 된다.
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
해당 코드는 롬복 어노테이션 프로세서가 사용할 대상(필드) 의 어노테이션을 명시적으로 선언하는것이다.
이걸로 Lombok 의 @RequiredArgsConstructor 와 @Qualifier 를 같이 사용하여도 문제가 발생하지 않는다.
'spring & boot > Spring & Spring Boot' 카테고리의 다른 글
[Spring boot] Spring Boot 라이브러리(Library) 개념 및 만들기(1) (1) | 2024.12.27 |
---|---|
[Spring Boot]OAuth2: Authorization-Server (Custom 인증 서버 구축)(2) (4) | 2024.11.20 |
[Spring boot] Spring Boot 3.x^ - Swagger 적용(2) (0) | 2024.11.14 |
[Spring boot] OAuth2: Authorization-Server (인증 서버 구축)(1) (1) | 2024.11.13 |
[Spring Boot] WireMock - API Test (0) | 2024.10.29 |
댓글