Skip to content

Improve documentation for @Autowired constructors #23263

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

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

Improve documentation for @Autowired constructors #23263

farazdurrani opened this issue Jul 9, 2019 · 6 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: documentation A documentation task
Milestone

Comments

@farazdurrani
Copy link

farazdurrani commented Jul 9, 2019

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

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 when I mark multiple constructors with @Autowired with one being required = true and other being marked as required = false:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'constructorInjectComponent': Invalid autowire-marked constructors: [public com.example.demo.ConstructorInjectComponent(com.example.demo.InjectionServiceThree)]. Found constructor with 'required' Autowired annotation: public com.example.demo.ConstructorInjectComponent(com.example.demo.InjectionServiceOne,com.example.demo.InjectionServiceTwo)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:322) ~[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]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1184) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at com.example.demo.MultipleConstructionInjectionApplication.main(MultipleConstructionInjectionApplication.java:10) [main/:na]

This is the code:

@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());
	}
}

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 or decided on label Jul 9, 2019
@sbrannen sbrannen added type: documentation A documentation task in: core Issues in core modules (aop, beans, core, context, expression) labels Jul 17, 2019
@sbrannen sbrannen changed the title Documentation on Autowiring needs improvement Improve @Autowired documentation for multiple constructors Jul 17, 2019
@sbrannen
Copy link
Member

Related to gh-21832

@sbrannen sbrannen self-assigned this Jul 17, 2019
@sbrannen sbrannen removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 17, 2019
@sbrannen sbrannen modified the milestones: 5.2 RC1, 5.1.9 Jul 17, 2019
@sbrannen sbrannen changed the title Improve @Autowired documentation for multiple constructors Improve documentation for @Autowired constructors Jul 17, 2019
@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-4.3.x labels Jul 17, 2019
sbrannen added a commit that referenced this issue Jul 18, 2019
Prior to this commit, there was some ambiguity surrounding semantics
for @Autowired constructors and `required = true`, especially for
multiple @Autowired constructors.

This commit improves the documentation in the Javadoc for @Autowired as
well as in the reference manual.

Closes gh-23263
sbrannen added a commit that referenced this issue Jul 18, 2019
Prior to this commit, there was some ambiguity surrounding semantics
for @Autowired constructors and `required = true`, especially for
multiple @Autowired constructors.

This commit improves the documentation in the Javadoc for @Autowired as
well as in the reference manual.

Closes gh-23263
@sbrannen
Copy link
Member

@farazdurrani, thanks for raising the issue.

The improved documentation can be seen in the latest snapshot for the reference manual and Javadoc.

sbrannen added a commit that referenced this issue Jul 18, 2019
@farazdurrani
Copy link
Author

farazdurrani commented Jul 18, 2019

No problem. The new wordings are clear. Thank you.

@ElmehdiBenh
Copy link

ElmehdiBenh commented Apr 1, 2020

Hello @sbrannen ,

I think that the problem is still there. In the new docs (https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/core.html#beans-autowired-annotation) we found:

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. However, if several constructors are available, at least one must be annotated with @Autowired in order to instruct the container which one to use.

I think that "at least one constructor must be annotated [...]" is not correct.

According to the container behavior, just one constructor must be annotated with @Autowired else (no @Autowired) the container looks for the default constructor.

@sbrannen
Copy link
Member

sbrannen commented Apr 1, 2020

@ElmehdiBenh, thanks for pointing that out!

I've opened #24838 to improve the documentation for such scenarios.

@ElmehdiBenh
Copy link

@sbrannen , thanks to you ;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

4 participants