Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Constructors marked with @Autowired not working #17455

Closed
farazdurrani opened this issue Jul 8, 2019 · 6 comments
Closed

Multiple Constructors marked with @Autowired not working #17455

farazdurrani opened this issue Jul 8, 2019 · 6 comments
Labels
for: external-project For an external project and not something we can fix status: invalid An issue that we don't feel is valid

Comments

@farazdurrani
Copy link

farazdurrani commented Jul 8, 2019

When multiple constructors are marked by @Autowired, it doesn't work. Although Spring documentation clearly says this:

Only one annotated constructor per class can be marked as required, but multiple non-required constructors can be annotated. In that case, each is considered among the candidates and Spring uses the greediest constructor whose dependencies can be satisfied — that is, the constructor that has the largest number of arguments. The constructor resolution algorithm is the same as for non-annotated classes with overloaded constructors, just narrowing the candidates to annotated constructors.

I get this error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'constructorInjectComponent': Invalid autowire-marked constructor: public com.example.demo.constructorinjection.constructorInjectComponent(com.example.demo.constructorinjection.InjectionServiceOne). Found constructor with 'required' Autowired annotation already: public com.example.demo.constructorinjection.constructorInjectComponent(com.example.demo.constructorinjection.InjectionServiceOne,com.example.demo.constructorinjection.InjectionServiceTwo) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:314) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1269) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]

This is my code:

@Component
public class ConstructorInjectComponent {   

private InjectionServiceOne injectionServiceOne;

private InjectionServiceTwo injectionServiceTwo;

@Autowired(required = true)
public constructorInjectComponent(InjectionServiceOne injectionServiceOne,
        InjectionServiceTwo injectionServiceTwo) {
    this.injectionServiceOne = injectionServiceOne;
    this.injectionServiceTwo = injectionServiceTwo;
}

@Autowired(required = false)
public constructorInjectComponent(InjectionServiceTwo injectionServiceTwo) {
    this.injectionServiceTwo = injectionServiceTwo;
}

@Autowired(required = false)
public constructorInjectComponent(InjectionServiceOne injectionServiceOne) {
    this.injectionServiceOne = injectionServiceOne;
}

@Scheduled(fixedRate=1000L)
public void allFieldsConstructorInjectionTest() {
    System.err.println("constructorInjection " + injectionServiceOne.method() + " " + injectionServiceTwo.method());
}
}

I have created a simple github repo to reproduce the issue.

OS: Ubuntu 18.04
JDK 8.

Spring Boot version 2.1.6

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 8, 2019
@wilkinsona
Copy link
Member

The behaviour in question is part of Spring Framework and is, I believe, working as designed. When you used required=true a single constructor must be annotated with @Autowired. In your particular case, your constructors are contradictory as you have one that indicates that both InjectionServiceOne and InjectionServiceTwo are required and two others that suggest that both are not required.

@wilkinsona wilkinsona added for: external-project For an external project and not something we can fix status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Jul 8, 2019
@farazdurrani
Copy link
Author

farazdurrani commented Jul 8, 2019

The behaviour in question is part of Spring Framework and is, I believe, working as designed. When you used required=true a single constructor must be annotated with @Autowired. In your particular case, your constructors are contradictory as you have one that indicates that both InjectionServiceOne and InjectionServiceTwo are required and two others that suggest that both are not required.

Okay @wilkinsona I changed my code. Still getting an error.

@Component
@EnableScheduling
public class ConstructorInjectComponent {

	private InjectionServiceOne injectionServiceOne;

	private InjectionServiceTwo injectionServiceTwo;
	
	private InjectionServiceThree injectionServiceThree;


	@Autowired(required = true)
	public ConstructorInjectComponent(InjectionServiceOne injectionServiceOne,
			InjectionServiceTwo injectionServiceTwo) {
		this.injectionServiceOne = injectionServiceOne;
		this.injectionServiceTwo = injectionServiceTwo;
	}

	@Autowired(required = false)
	public ConstructorInjectComponent(InjectionServiceThree injectionServiceThree) {
		this.injectionServiceThree = injectionServiceThree;
	}
	
	@Scheduled(fixedRate = 1000L)
	public void allFieldsConstructorInjectionTest() {
		System.err.println("constructorInjection " + injectionServiceOne.method() + " " + injectionServiceTwo.method() + injectionServiceThree.method());
	}

}

Why is it not working? Is the documentation wrong?

New code changes reflect in the github repo

@mbhave
Copy link
Contributor

mbhave commented Jul 9, 2019

@farazdurrani As @wilkinsona already mentioned, when you used required=true only a single constructor must be annotated with @Autowired. If you think the documentation needs to be improved, you can create an issue in the Spring Framework issue tracker as the behavior in question is part of Spring Framework.

@farazdurrani
Copy link
Author

@mbhave thanks, I will do that.

@farazdurrani
Copy link
Author

Here I raised the issue.

@Maxrunner
Copy link

Maxrunner commented Dec 10, 2019

@farazdurrani As @wilkinsona already mentioned, when you used required=true only a single constructor must be annotated with @Autowired. If you think the documentation needs to be improved, you can create an issue in the Spring Framework issue tracker as the behavior in question is part of Spring Framework.

Autowired is required by default right? the documentation is not very well written. All of these will fail:
two constructors with @Autowired, one constructor with @Autowired and another with ( @Autowired(required=true) or @Autowired(required=false) ,
one constructor with @Autowired(required=true) and another with @Autowired(required=false) (this is the one that causes more confusion because the way the documentation is written it seems you can have one required and another not required)

The only instance where you can have multiple constructors annotated is if all of them are @Autowired(required=false)

Look at this question in one exam (not official) :
There can be multiple constructor methods annotated with @Autowired but only 1 of them can be set as required
This false right after the .."there can be multiple constructor methods annotated with @Autowired...." and in this exam they consider this true.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

5 participants