7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

lombokで@RequiredArgsConstructorに@Qualifierを渡したいときのlombok.config

Posted at

概要

このエントリでは、lombok@RequiredArgsConstructorを使ってコンストラクタインジェクションをしたいときの方法について扱います。

エントリの動機

ちょっとエッジケースかもしれませんが、とはいえやりたくなるケースがほかの方にもあるかもしれませんので参考になればということで。

TL;DR;

タイトルのことをしたい場合には、下記の1行をlombok.configに追加します。

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

説明

使うコード

import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.Value;

@RequiredArgsConstructor
@ToString
@Value
public class MyComponent {
    private final String name;
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyComponentConfiguration {
    @Bean(name="myComponentA")
    public MyComponent myComponentA() {
        return new MyComponent("name A");
    }

    @Bean(name="myComponentB")
    public MyComponent myComponentB() {
        return new MyComponent("name B");
    }
}

@Service
@RequiredArgsConstructor
@Slf4j
public class MyService {
    @Qualifier("myComponentA")
    private final MyComponent myComponentAdash;
    @Qualifier("myComponentB")
    private final MyComponent myComponentBdash;

    public void sayHello() {
        log.info("myComponentA->" + myComponentAdash.getName());
        log.info("myComponentB->" + myComponentBdash.getName());
    }
}

この状態で起動しようとすると下記のエラーとなります。

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-07-19 22:00:45.510 ERROR 16076 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in info.beambitious.sandbox.lombokqualifiertest.MyService required a single bean, but 2 were found:
        - myComponentA: defined by method 'myComponentA' in class path resource [info/beambitious/sandbox/lombokqualifiertest/MyComponentConfiguration.class]
        - myComponentB: defined by method 'myComponentB' in class path resource [info/beambitious/sandbox/lombokqualifiertest/MyComponentConfiguration.class]


Action:

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


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':bootRun'.
> Process 'command 'C:\Java\Amazon Corretto\jdk1.8.0_222\bin\java.exe'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 8s
3 actionable tasks: 2 executed, 1 up-to-date

lombokの@RequiredArgsConstructorで、MyComponent型のフィールド2つに対してコンストラクタインジェクションで値を指定しようとしていますが、@Beanのnameで指定しているものと違う値であり判別しようがない状況です。

MyServiceをdelombokして、自動生成されるコードを眺めてみます。

java -jar .\lombok.jar delombok --print .\src\main\java\info\beambitious\sandbox\lombokqualifiertest\MyService.java

lombokの@RequiredArgsConstructorが作ってくれたコンストラクタには@Qualifierがついていません。、

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class MyService {
    @java.lang.SuppressWarnings("all")
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MyService.class);
    @Qualifier("myComponentA")
    private final MyComponent myComponentAdash;
    @Qualifier("myComponentB")
    private final MyComponent myComponentBdash;

    public void sayHello() {
        log.info("myComponentA->" + myComponentAdash.getName());
        log.info("myComponentB->" + myComponentBdash.getName());
    }

    @java.lang.SuppressWarnings("all")
    public MyService(final MyComponent myComponentAdash, final MyComponent myComponentBdash) {
        this.myComponentAdash = myComponentAdash;
        this.myComponentBdash = myComponentBdash;
    }
}

困ったなぁと思ってStackOverflowを眺めていたら、is it possible to add qualifiers in @RequiredArgsConstructor(onConstructor = @__(@Autowired))?というのを見つけました。

(参考):上記記事内で参照されているGitHubのIssue745

TL;DR;のところで紹介した通りの値をlombok.configに指定して、delombokしてみます。

    @java.lang.SuppressWarnings("all")
    public MyService(@Qualifier("myComponentA") final MyComponent myComponentAdash, @Qualifier("myComponentB") final MyComponent myComponentBdash) {
        this.myComponentAdash = myComponentAdash;
        this.myComponentBdash = myComponentBdash;
    }

今度は、@Qualifierがコンストラクタ内に記述されています。

cleanしてから動かすと、プログラムが正常に動きます。

gradlew clean
gradlew bootRun

(中略)

2020-07-19 22:18:53.006  INFO 14200 --- [           main] i.b.s.l.LombokQualifierTestApplication   : Started LombokQualifierTestApplication in 1.658 seconds (JVM running for 2.305)
2020-07-19 22:18:53.010  INFO 14200 --- [           main] i.b.s.lombokqualifiertest.MyService      : myComponentA->name A
2020-07-19 22:18:53.011  INFO 14200 --- [           main] i.b.s.lombokqualifiertest.MyService      : myComponentB->name B

おわりに

別解として、MyService内のフィールドの名前を変えてあげても動くのですが、このエントリではQualifierで明示的に指定する方法について取り上げています。

    private final MyComponent myComponentA;
    private final MyComponent myComponentB;

このエントリで使用したコードは、GitHubに置いてあります。

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?