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 .springframework.stereotype.Component;
import .springframework.context.annotation.Configuration;
import .springframework.context.annotation.ComponentScan;
import .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 .springframework.context.support.AbstractApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: .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 .springframework.beans.BeanInstantiationException: Failed to instantiate [autowiringp1]: No default constructor found; nested exception is java.lang.NoSuchMethodException: autowiringp1.<init>()
Exception in thread "main" .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 .springframework.beans.BeanInstantiationException: Failed to instantiate [autowiringp1]: No default constructor found; nested exception is java.lang.NoSuchMethodException: autowiringp1.<init>()
at .springframe
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 .springframework.stereotype.Component;
import .springframework.context.annotation.Configuration;
import .springframework.context.annotation.ComponentScan;
import .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 .springframework.context.support.AbstractApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: .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 .springframework.beans.BeanInstantiationException: Failed to instantiate [autowiringp1]: No default constructor found; nested exception is java.lang.NoSuchMethodException: autowiringp1.<init>()
Exception in thread "main" .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 .springframework.beans.BeanInstantiationException: Failed to instantiate [autowiringp1]: No default constructor found; nested exception is java.lang.NoSuchMethodException: autowiringp1.<init>()
at .springframe
Share
Improve this question
edited 2 days ago
dani-vta
7,0557 gold badges49 silver badges65 bronze badges
asked 2 days ago
samhsamh
313 bronze badges
2 Answers
Reset to default 2Only 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);
}
}
According to Using @Autowired from the framwork reference, Spring automatically matches dependencies by type with no @Autowired
configuration, only if a bean exhibits a single arg-constructor.
As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with.
In your case, comp1
provides multiple constructors. Therefore, you need to tell Spring how you want to wire the bean's dependencies. Without any form of wiring configuration (@Autowired
, @Resource
, or @Inject
), Spring tries to instantiate the bean via a no-arg constructor, but since there isn't one, a BeanCreationException
is thrown.
To fix your issue, you could simply annotate comp1
's second constructor with @Autowired
.
@Component
class Foo{
String name="Foo";
}
@Component
class Boo{
String name="Bar";
}
@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;
}
@Autowired
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);
}
}