I’m trying to use constructor injection. I understand that when Spring finds multiple constructors, it will try to choose the one with the largest number of arguments where the types match Spring-managed beans. However, in this example, it fails. Normally, it should call the second constructor.
package autowiring;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@Configuration
@ComponentScan
class config{
}
@Component
public class comp1{
Foo ab;
Boo bo;
public comp1(Foo ab,String a,X d){
System.out.println("constructor 1 called");
this.ab=ab;
}
public comp1(Foo ab,Boo bo){
System.out.println("constructor 2 called");
this.ab=ab;
this.bo=bo;
}
public static void main(){
var ctx=new AnnotationConfigApplicationContext(config.class);
}
}
@Component
class Foo{
String name="Foo";
}
@Component
class Boo{
String name="Bar";
}
class X{
}
The output:
amdi@debian:~/Documents/learn/java2/test$ java -jar target/my*.jar
Feb 18, 2025 6:39:04 AM org.springframework.context.support.AbstractApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'comp1' defined in URL [jar:file:/home/lmhamdi/Documents/learn/java2/test/target/my-app-1.0.0.jar!/autowiring/comp1.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [autowiring.comp1]: No default constructor found; nested exception is java.lang.NoSuchMethodException: autowiring.comp1.<init>()
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'comp1' defined in URL [jar:file:/home/lmhamdi/Documents/learn/java2/test/target/my-app-1.0.0.jar!/autowiring/comp1.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [autowiring.comp1]: No default constructor found; nested exception is java.lang.NoSuchMethodException: autowiring.comp1.<init>()
at org.springframe
Only one constructor of any given bean class may declare
@Autowired
with the required attribute set totrue
, indicating the constructor to autowire when used as a Spring bean. As a consequence, if the required attribute is left at its default valuetrue
, only a single constructor may be annotated with@Autowired
. If multiple constructors declare the annotation, they will all have to declarerequired=false
in order to be considered as candidates for autowiring (analogous to autowire=constructor in XML). The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen. If none of the candidates can be satisfied, then a primary/default constructor (if present) will be used. Similarly, if a class declares multiple constructors but none of them is annotated with@Autowired
, then a primary/default constructor (if present) will be used. If a class only declares a single constructor to begin with, it will always be used, even if not annotated. Note that an annotated constructor does not have to be public.
This is from the referenece guide and, is pretty clear on the resolution strategy. It explicitly mentions what happens if there are multiple annotations and none of them are annotation with @Autowired
. That is the part that I highlighted.
What you state is true but only for constructors that are annotated with @Autowired
. Hence if you want what you already think is happening you need to autowire your constructors with @Autowired(required=false)
and then Spring will detect the best matching one.
@Component
public class comp1{
Foo ab;
Boo bo;
@Autowired(required=false)
public comp1(Foo ab,String a,X d){
System.out.println("constructor 1 called");
this.ab=ab;
}
@Autowired(required=false)
public comp1(Foo ab,Boo bo){
System.out.println("constructor 2 called");
this.ab=ab;
this.bo=bo;
}
public static void main(){
var ctx=new AnnotationConfigApplicationContext(config.class);
}
}