55

I'm developing a Java Spring application. I have some fields in my application which are configured using a .yml config file. I would like to import those values using an @Value annotation on the fields in question. I would also like to use the best-practice of constructor injection rather than field injection, but I would like to write my constructor using Lombok rather than manually. Is there any way to do all these things at once? As an example, this doesn't work but is similar to what I want to do:

@AllArgsConstructor
public class my service {
    @Value("${my.config.value}")
    private String myField;

    private Object myDependency;

    ...
}

In this case, what I want is Lombok to generate a constructor which sets only myDependency, and for myField to be read from my config file.

Thanks!

Ertai87
  • 1,156
  • 1
  • 15
  • 26

2 Answers2

54

You need @RequiredArgsConstructor and mark myDependency as final. In this case, Lombok will generate a constructor based on 'required' final filed as argument, for example:

@RequiredArgsConstructor
@Service
public class MyService {

    @Value("${my.config.value}")
    private String myField;

    private final MyComponent myComponent;

    //...
}

That is equal the following:

@Service
public class MyService {

    @Value("${my.config.value}")
    private String myField;

    private final MyComponent myComponent;

    public MyService(MyComponent myComponent) { // <= implicit injection
        this.myComponent = myComponent;
    } 

    //...
}

Since here is only one constructor, Spring inject MyComponent without the explicit use of the @Autowired annotation.

Cepr0
  • 28,144
  • 8
  • 75
  • 101
  • How does Spring know which Bean to inject in the field `myComponent` ? Usually you would have an annotation like `@Autowired` for that, wouldn't you ? – Gustavo Passini Sep 13 '18 at 22:41
  • 4
    @GustavoPassini [If a bean has one constructor, you can omit the @Autowired](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html) – Cepr0 Sep 14 '18 at 07:42
  • 5
    for those who don't want to limit themselves with the final keyword, they can use @NonNull (lombok) annotation. – Mr.Q May 18 '19 at 06:23
  • Won't this approach take away readability in many cases where you have more than let's say 10 variables, out of which 1 or 2 are getting populated using some code. You would never know which are autowired and which are populated in code? – Bilbo Baggins Aug 18 '20 at 10:50
  • 2
    What about the `myField` which has `@Value` annotation? – manikanta Jan 27 '21 at 08:58
  • 3
    How you can test the class with that myField? – Andrii Bobrov Feb 08 '21 at 10:26
  • How can I use with @Builder? REpository instance is null – withoutOne Feb 24 '21 at 14:33
  • I wonder, how we can make this work if we set myField as final ??? – Mr.Q Nov 23 '21 at 08:29
45

Make sure you are using at least version 1.18.4 of Lombok. And that you have your desired annotation added to the lombok.config file.

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

Here is your class:

@AllArgsConstructor(onConstructor = @__(@Autowired))
public class MyService{

    @Value("${my.config.value}")
    private String myField;

    private Object myDependency;
}

And here is the Lombok generated class:

public class MyService {

@Value("${my.config.value}")
private String myField;

private Object myDependency;

@Autowired
@Generated
public MyService(@Value("${my.config.value}") final String myField, final Object myDependency) {
    this.myField = myField;
    this.myDependency = myDependency;
}

PS: Make sure you have the lombok.config file under /src/main/java folder. I tried adding it to /src/main/resources and it did not work.

Response taken from Lombok - retain field's annotation in constructor input params.

Laurel
  • 5,965
  • 14
  • 31
  • 57
octavian
  • 686
  • 6
  • 10