ChatGPT解决这个技术问题 Extra ChatGPT

How to set @Autowired constructor params as "required=false" individually

I'm using the @Autowired annotation under a @Configuration class constructor.

@Configuration
public class MyConfiguration {

   private MyServiceA myServiceA;
   private MyServiceB myServiceB

   @Autowired
   public MyConfiguration(MyServiceA myServiceA, MyServiceB myServiceB){
     this.myServiceA = myServiceA;
     this.myServiceB = myServiceB;    
   }
}

As the Spring documentation sais, I'm able to declare whether the annotated dependency is required.

If I mark the @Autowired annotation under the constructor as required=false, I'm saying that the two services to be autowired are not required (as the Spring documentation says):

@Autowired(required = false)
public MyConfiguration(MyServiceA myServiceA, MyServiceB myServiceB){
  this.myServiceA = myServiceA;
  this.myServiceB = myServiceB;   
}

From Spring documentation:

In the case of multiple argument methods, the 'required' parameter is applicable for all arguments.

How can I set the required attribute to each constructor parameter individually? Is necessary to use @Autowired annotation under every field?

Regards,

You should use : - Constructor Injection for Mandatory dependencies - Setters Injection for optional dependencies

S
Strelok

If you're using Java 8 and Spring Framework 4, you can use Optional.

@Autowired
public MyConfiguration(Optional<MyServiceA> myServiceA, Optional<MyServiceB> myServiceB){
  myServiceA.ifPresent(service->{this.myServiceA = service});
  myServiceB.ifPresent(service->{this.myServiceB = service});   
}

@AndrewTobilko surely the OP is checking for nulls if he is considering an "optional" dependency. You can just have optional fields too. Many ways to ensure you will not get an NPE.
I prefer not to use Optional, but thanks for your answer ;)
Optional is the way forward, IMO. Makes it really easy to see from the constructor what the dependencies are and which are optional, without having to look through all the setters. I think in the constructor body this.myServiceA = myServiceA.orElse(null) is a little easier on the eye than myServiceA.ifPresent(service->{this.myServiceA = service}).
Alternatively: this.myServiceA = myServiceA.orElse(this.myServiceA);
This looks like a perfect thing to do, but Intellij's Idea gives a warning about using Optional in method parameter.
C
Community

Explicit approach

Basically, you have a bean which have some required and optional dependencies. The recommended way of handling this scenario, not only configuration beans but any other, is to create a constructor only for mandatory dependencies and use setter injection for optional ones.

public class MyConfiguration {

   private final MyServiceA myServiceA;
   private MyServiceB myServiceB

   @Autowired
   public MyConfiguration(MyServiceA myServiceA){
     this.myServiceA = myServiceA;   
   }

   @Autowired(required = false)
   public void setMyServiceB(MyServiceB myServiceB) {
     this.myServiceB = myServiceB;
   }

}

With this approach you can easily unit test the class without necessity for any mocking library. You can create an object in testing state using the constructor and optional setters.

Putting @Autowired(required = false) directly on the field and removing the setter will also work, but since you are using the constructor injection, I assume you want to state dependencies more explicitly.

Additional idea

You can also consider using the Optional type to wrap not mandatory dependencies. It is common among developers to assume that if a class has a property, it should be set, which is obviously not right in your scenario. To mark the possibility of absence for particular dependencies more clear you probably can use Optional:

public class MyConfiguration {

   private final MyServiceA myServiceA;
   private Optional<MyServiceB> myServiceB

   @Autowired
   public MyConfiguration(MyServiceA myServiceA){
     this.myServiceA = myServiceA;
     this.myServiceB = Optional.empty();   
   }

   @Autowired(required = false)
   public void setMyServiceB(MyServiceB myServiceB) {
     this.myServiceB = Optional.ofNullable(myServiceB);
   }

}

Some people are against using the Optional type for class properties (mainly because of this answer from Brian Goetz), but at the end of the day it should be the decision made by the whole team that is going to work on the project.


Sorry, but as the Spring documentation sais: "Only one constructor (at max) of any given bean class may carry this annotation, indicating the constructor to autowire when used as a Spring bean"
Note there is only one constructor in the example. The second annotation is on the setter method. You can annotate as many setters as you like.
My fault, I didn't see the second method as a setter. Of course, I know that I'm able to annotate as many setters as I want and as many properties as I want... but is that what i'm trying to avoid!
IMHO this is a very clear and nice approach and should be "the one to go".
C
Czar

Since Spring 4.3.0.RC1 you can do this:

public MyConfiguration(MyServiceA myServiceA, @Autowired(required = false) MyServiceB myServiceB){
  this.myServiceA = myServiceA;
  this.myServiceB = myServiceB;   
}

As ElementType.PARAMETER was added as annotation target.

From Spring 5.0 @Autowired(required = false) can also be replaced by @Nullable or a Kotlin nullable type can be used without any annotation, e.g. MyServiceB?


I use it exactly like that but along with @Qualifier and still get No qualifying bean of type 'java.util.Properties' available: expected at least 1 bean which qualifies as autowire candidate.
Х
Хакан Хюсеин

As of Spring Framework 5.0, you can also use a @Nullable annotation (of any kind in any package — for example, javax.annotation.Nullable from JSR-305):

@Configuration
public class MyConfiguration {

   private MyServiceA myServiceA;
   private MyServiceB myServiceB

   @Autowired
   public MyConfiguration(@Nullable MyServiceA myServiceA, MyServiceB myServiceB){
     this.myServiceA = myServiceA;
     this.myServiceB = myServiceB;    
   }
}