[JAVA] lombok.config when you want to pass @Qualifier to @RequiredArgsConstructor with lombok

Overview

This entry deals with how to do constructor injection with @RequiredArgsConstructor at lombok.

Motivation for entry

It may be a little edge case, but there may be other people who want to do it, so I hope it will be helpful.

TL;DR;

If you want to do something about the title, add the following line to lombok.config.

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

Description

Code to use

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());
    }
}

If you try to start it in this state, the following error will occur.

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

In lombok's @RequiredArgsConstructor, I'm trying to specify a value for two fields of MyComponent type by constructor injection, but the value is different from the one specified by @Bean's name and I can't tell.

Delombok MyService and take a look at the automatically generated code.

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

The constructor created by lombok's @RequiredArgsConstructor does not have @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;
    }
}

When I was looking at StackOverflow because I was in trouble, [is it possible to add qualifiers in @RequiredArgsConstructor (onConstructor = @__ (@Autowired))?](Https://stackoverflow.com/questions/38549657/is-it -possible-to-add-qualifiers-in-requiredargsconstructoronconstructor) I found.

(Reference): Issue745 on GitHub referenced in the above article.

Specify the value as introduced in TL; DR; in lombok.config and try delombok.

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

This time, @Qualifier is written in the constructor.

If you clean it and then run it, the program will work normally.

gradlew clean
gradlew bootRun

(Omission)

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

in conclusion

As an alternative, it works if you rename the fields in MyService, but this entry covers how to explicitly specify them in the Qualifier.

    private final MyComponent myComponentA;
    private final MyComponent myComponentB;

The code used in this entry can be found on GitHub. ](Https://github.com/hrkt/lombok-qualifier-test/releases/tag/0.0.1)

Recommended Posts

lombok.config when you want to pass @Qualifier to @RequiredArgsConstructor with lombok
When you want to explicitly write OR or AND with ransack
When you want to bind InputStream in JDBI3
When you want to use the method outside
[Ruby] When you want to replace multiple characters
If you want to use Mockito with Kotlin, use mockito-kotlin
When you want to dynamically replace Annotation in Java8
You can't (yet) pass arguments to buildkit with docker-compose
A memo to check when you try to use Lombok
Code to use when you want to process Json with only standard library in Java
[Ruby + Rails] When you want to register in Mailchimp's mail list together with user registration
What to do when you launch an application with rails
How to write in Model class when you want to save binary data in DB with PlayFramework
When you want to change the MySQL password of docker-compose
Delegate is convenient to use when you want to reuse parts
I want to pass the startup command to postgres with docker-compose.
ProxyFactory is convenient when you want to test AOP in Spring!
I want to avoid OutOfMemory when outputting large files with POI
When you want to ZIP download the image data saved locally
When you have introduced devise but want to add more columns
[Rails] I want to add data to Params when transitioning with link_to
Practice to use when you want to execute different processing groups serially
The first thing to do when you want to be happy with Heroku on GitHub with Eclipse in Java
A memo when you want to clear the time part of the calendar
When you want Rails to disable a session for a specific controller only
If you want to make a zip file with Ruby, it's rubyzip.
I want to use DBViewer with Eclipse 2018-12! !!
Basic Rails commands you want to learn
What to do when you want to delete a migration file that is "NO FILE"
When you want to notify an error somewhere when using graphql-spring-boot in Spring Boot
Code used when you want to process Json with only the standard library in Java (improved version) gson unnecessary