Lombokの@RequiredArgsConstructorにSpringBootの@Qualifierを渡したいときのlombok.copyableAnnotationsの設定方法

エキサイト株式会社メディア事業部エンジニアの佐々木です。Lombok@RequiredArgsConstructorが便利で弊社では多用しているのですが、@Qualifierを使用したいときに、コンストラクタを書かないといけないのが面倒でした。Lombokのドキュメントを眺めていたところ解決方法があったので、ご紹介します。

DIの登録コード

下記のようにRestClientを複数個設定することは、結構あるかなと思います。

@Configuration
public class RestClientConfig {
    
    @Bean("A_site_API")
    public RestClient A_site_API() {

        return RestClient.builder()
                .baseUrl("https://a.com/api")
                .defaultHeaders(e -> e.addAll(CollectionUtils.toMultiValueMap(Map.of(
                        HttpHeaders.AUTHORIZATION, List.of("Bearer " + "A_Site_accessToken"),
                        HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON_VALUE)))))
                .build();
    }

    @Bean("B_site_API")
    public RestClient B_site_API() {

        return RestClient.builder()
                .baseUrl("https://b.com/api")
                .defaultHeaders(e -> e.addAll(CollectionUtils.toMultiValueMap(Map.of(
                        HttpHeaders.AUTHORIZATION, List.of("Bearer " + "B_Site_accessToken"),
                        HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON_VALUE)))))
                .build();
    }
}

設定はいいのですが、呼び出しを行うときに、同じ型なので@Qualifierで解決する必要があります。

DIの呼び出しコード

通常下記のような呼び出しを書きますが、下記だとSpringBoot起動時にエラーになってしまいます。

@Repository
@RequiredArgsConstructor
public class A_SiteRepositoryImpl implements A_SiteRepository {

    private final RestClient restClient;

    @Override
    public ResponseEntity<String> createMeeting(CreateRoomModel form) {
        return restClient.get().uri("/xxlist").retrieve().toEntity(String.class);
    }
}

=====
下記のようなエラーログがでます

Parameter 0 of constructor in jp.co.excite.x_dental.api.repository.webmeeting.MeetingRepositoryImpl required a single bean, but 3 were found:
    - A_site_API: defined by method 'A_site_API' in class path resource [jp/co/excite/x_dental/api/config/RestClientConfig.class]
    - B_site_API: defined by method 'B_site_API' in class path resource [jp/co/excite/x_dental/api/config/RestClientConfig.class]
    - whereByRestClient: defined by method 'whereByRestClient' in class path resource [jp/co/excite/x_dental/api/config/RestClientSetting.class]

では、フィールドに@Qualifierアノテーションをつけて起動しても、やはりSpringBoot起動時にエラーになってしまいます。

@Repository
@RequiredArgsConstructor
public class A_SiteRepositoryImpl implements A_SiteRepository {

    @Qualifier("A_site_API")
    private final RestClient restClient;

    @Override
    public ResponseEntity<String> createMeeting(CreateRoomModel form) {
        return restClient.get().uri("/xxlist").retrieve().toEntity(String.class);
    }
}

=====
下記のようなエラーログがでます

Parameter 0 of constructor in jp.co.excite.x_dental.api.repository.webmeeting.MeetingRepositoryImpl required a single bean, but 3 were found:
    - A_site_API: defined by method 'A_site_API' in class path resource [jp/co/excite/x_dental/api/config/RestClientConfig.class]
    - B_site_API: defined by method 'B_site_API' in class path resource [jp/co/excite/x_dental/api/config/RestClientConfig.class]
    - whereByRestClient: defined by method 'whereByRestClient' in class path resource [jp/co/excite/x_dental/api/config/RestClientSetting.class]

このエラーはlombok.configの設定で回避できます。

lombok.configでの設定

lombok.configで下記の設定を行うと、上記のエラーがなくなり起動ができます。

lombok.copyableAnnotations+=org.springframework.beans.factory.annotation.Qualifier

DIだけのためにコンストラクタを書かなくてもいいので、お手軽です。

まとめ

Lombokを使っていますけど、ドキュメントの読み込みは大事だなと思いました。とても便利なのでぜひお試しください。

最後に

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com