_Migrated from Google Code (issue 710)_
:bust_in_silhouette: jorn86 :clock8: Jul 21, 2014 at 13:30 UTC
What steps will reproduce the problem?
What is the expected output? What do you see instead?
Expected: constructor with annotation on the parameter
Actual: constructor with the parameter, but without annotation
What version of the product are you using? On what operating system?
Lombox 1.14.2, Eclipse Kepler, Windows 7
Please provide any additional information below.
This should be obvious from the example, but when I'm generating a constructor for a Guice-created class, I'd like the qualifier annotation to be preserved in the constructor.
_End of migration_
+1 (I wish GitHub would have a vote feature)
Other valid use case: @Nullable
+1
I think this breaks null analysis in IDEs because the generated constructor/getters/setters do not have the nullable/notnull annotations from the fields.
I'm experiencing this when trying to inject a Spring @Value like so:
@Component
@RequiredArgsConstructor
public class MySpringComponent {
@Value("${config.param.value}")
private final String someConfigParam;
}
When I bootRun the app, Spring complains that it cannot instantiate a bean of type String.
I expect Lombok to generate a constructor that includes this annotation.
Thanks!
hey @myoffe
We have been using a similar approach to what you described successfully on lombok 1.16.20. I guess you were inspired by these articles, or one of the earlier:
However, there is one thing to mention your approach:
Putting a straight up @Value in your @Component is NOT a good practice (https://tuhrig.de/why-using-springs-value-annotation-is-bad/), as it encourages multiple other components maybe using the same @Value.
you should replace it with stuff like:
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MySpringComponent {
@NonNull private final Config someConfig;
}
where your config is like following (which wouldn't work):
@Configuration
@RequiredArgsConstructor
public class Config {
@Value("${config.param.value}") private final String someConfigParam;
}
Why wouldn't it work? The reason behind it is that what you want is (removing lombok stuff):
@Configuration
public class Config {
private final String someConfigParam;
public Config(@Value("${config.param.value}" String someConfigParam){
this.someConfigParam = someConfigParam;
}
}
but what you actually end up with is:
@Configuration
public class Config {
@Value("${config.param.value}") private final String someConfigParam;
public Config(String someConfigParam){ //Spring has no idea how to wire this property now
this.someConfigParam = someConfigParam;
}
}
But why doesn't lombok simply copy the Field annotation into the constructor?The reason why it is not so simple to fix is that, as you can see:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
The @Value annotation by Spring is rather permissive in terms where it can be placed. In other cases, however, an annotation that is a FIELD annotation might not be having the PARAMETER or METHOD Target, so it would be impossible to just copy the Field annotation into the constructor parameter. Hence, the ticket/feature/issue that we are currently discussing.
My suggestion to the lombok maintainers is to identify places where it is possible (and desirable to do so)
@NonNull is nice to be both on a field and copied on a constructor (and setters, which we already have as a functionality)@org.springframework.beans.factory.annotation.Value is also nice to be both on a field an be copied on a constructor with a RequiredArgsConstructorbut sometimes, there are annotations which either can't be copied onto the constructor as they are FIELD only annotations, or we simply don't want to do that. So I guess this requires some consideration on the API to be exposed so we have some fine tuning to turn on or off.
Hey @lc-nyovchev, thanks for your comment!
I agree that the separate configuration service is the right approach.
Also, thinking about it more, I understand that it's not trivial at all to assume that annotations will go into the constructor.
By the way: if you have a Spring bean like this:
@Component
@RequiredArgsConstructor
public class MyBean {}
and you only have one constructor (the generated one), you don't need to add the onConstructor = @__(@Autowired)) part, Spring will automatically use that constructor for injecting.
Hey guys, I was recently revisiting this one, and was thinking, can we maybe develop an experimental feature or a property of @RequiredArgsConstructor that sees what annotations are placed on instance variables, and if they can be applied to constructor, they are applied on the generated constructor as well? That'd work for @NonNull and @Qualifier which is what 99% of the people bumping this thread are trying to achieve.
E.g
@RequiredArgsConstructor(onConstructor = @__({@Autowired}), copyInstanceVarAnnotations = true)
public class MyClass {
@NonNull @Qualifier(name = "beanie") private Beanie beanie;
@NonNull @Qualifier(name = "beanie2") private Beanie beanie2;
@NonNull @NonCopyableAnnotation private Stuff stuff; //the NonCopyableAnnotation doesn't have METHOD retention level, so won't be copied
}
would generate something like that:
public class MyClass {
@Autowired
public MyClass(@Qualifier(name="beanie") Beanie beanie, @Qualifier(name="beanie2") Beanie beanie2, Stuff stuff) {
if (beanie == null){
throw new NullPointerException(...);
}
.......
}
}
This would also be useful for Jackson-compatible immutable DTOs that have all-final fields annotated with @JsonProperty("fieldName"), where the constructor parameters also need the annotation
@lc-nyovchev Lombok is mainly for POJOs, not for dependency targets
Besides, if you want thread safe code, write explicit constructor with final fields. The value of copying field level annotations will be useful(in the loose sense of the word) w.r.t POJO validation (plus what @Felk mentioned), not dependency targets
Instead of writing this ugly mess
@RequiredArgsConstructor(onConstructor = @__({@Autowired}), copyInstanceVarAnnotations = true)
to get what we want for dependency targets, simple java constructor in spring world is both safe, explcit & far superior option
In the next release, it is possible to configure what annotation types should be copied from the field to the parameters. It is already available in the edge release. Feedback is welcome.
Works great! This is going to make guice + lombok very slick.
One issue I'm having is figuring out how to add an inner class annotation to lombok.config? I've tried mypackage.MyClass$MyInnerAnnotation but no dice?
@benjaminjbachman
I've tried mypackage.MyClass$MyInnerAnnotation but no dice?
Haven't you tried mypackage.MyClass.MyInnerAnnotation? No idea, if it works, but that's what I'd try.
@rspilker can you give an example how to achieve that in the latest edge release?
Assuming I have a class like this:
@AllArgsConstructor
class A{
@Qualifier
private final Pojo pojo;
}
what is the configuration I should make to have @Qualifier on the generated constructor's parameter.
@benjaminjbachman
I've tried mypackage.MyClass$MyInnerAnnotation but no dice?
Haven't you tried
mypackage.MyClass.MyInnerAnnotation? No idea, if it works, but that's what I'd try.
I did try that too; did not work.
@rspilker can you give an example how to achieve that in the latest edge release?
Assuming I have a class like this:
@AllArgsConstructor
class A{
@qualifierwhat is the configuration I should make to have @qualifier on the generated constructor's parameter.
Make a lombok.config file with
lombok.copyableAnnotations += mypackage.qualifier where mypackage.qualifier is the full class name.
@benjaminjbachman Thanks it works.
One issue I'm having is figuring out how to add an inner class annotation to lombok.config? I've tried mypackage.MyClass$MyInnerAnnotation but no dice?
@rspilker any thoughts on this issue? is this a bug or am I doing the config wrong?
I suggest closing the ticket as it is possible now to achieve everything we ever wanted:
Thanks a bunch for the great implementation @rspilker !
Wrote a small project to demonstrate it working:
https://github.com/lc-nyovchev/lombok-spring-qualifier
Most helpful comment
Hey guys, I was recently revisiting this one, and was thinking, can we maybe develop an experimental feature or a property of @RequiredArgsConstructor that sees what annotations are placed on instance variables, and if they can be applied to constructor, they are applied on the generated constructor as well? That'd work for @NonNull and @Qualifier which is what 99% of the people bumping this thread are trying to achieve.
E.g
would generate something like that: