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

Should @Configuration mark @Inject fields and setters as satisfied (injected)? [SPR-14180] #18751

Open
spring-projects-issues opened this issue Apr 15, 2016 · 3 comments
Labels
in: core type: enhancement

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Apr 15, 2016

Christian Hersevoort opened SPR-14180 and commented

Somewhat related to my other issue: #18750 (It's a different way to solve the same problem)

The case is as follows:

Beans:

  • MyParentBean
  • MyChildBeanA implements MyChildBean
  • MyChildBeanB implements MyChildBean
@Configuration
public class MyBeanConfiguration
{
	@Bean
	public MyParentBean myParentBean(MyChildBeanA childBeanA)
	{
		MyParentBean myParentBean = new MyParentBean();
		myParentBean.setMyChild(childBeanA);
		return myParentBean;
	}
}

@Component
public class MyParentBean
{
	private MyChildBean myChild;

	@Inject
	public void setMyChild(MyChildBean myChild)
	{
		this.myChild = myChild;
	}
}

I expect the MyParentBean.myChild field to only be Injected by the @Configuration, and not by both ConfigurationClassPostProcessor and the AutowiredAnnotationBeanPostProcessor.

Expected:

  1. ConfigurationClassPostProcessor calls MyBeanConfiguration
  2. MyParentBean is created and field/property myChild is set.
  3. myChild is marked as satisfied(Injected)

What happens:

  1. ConfigurationClassPostProcessor calls MyBeanConfiguration
  2. MyParentBean is created and field/property myChild is set.
  3. AutowiredAnnotationBeanPostProcessor tries to Inject myChild again, but fails and throws a NoUniqueBeanDefinitionException

Changing between field and property injection doesn't help either e.g:

@Component
public class MyParentBean
{
	@Inject
	private MyChildBean myChild;
}

Changing @Bean to @Bean(autowire = Autowire.NO) doesn't help either, e.g:

@Configuration
public class MyBeanConfiguration
{
	@Bean(autowire = Autowire.NO)
	public MyParentBean myParentBean(MyChildBeanA childBeanA)
	{
		MyParentBean myParentBean = new MyParentBean();
		myParentBean.setMyChild(childBeanA);
		return myParentBean;
	}
}

Is this expected behavior? I expect org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#checkPropertySkipping to be true and skip injection.

If this is expected behavior: is there a way to prevent @Bean to be injected again by the AutowiredAnnotationBeanPostProcessor?


Issue Links:

  • #18750 Inconsistency between property Injection and setter Injection causes NoUniqueBeanDefinitionException
  • #18854 Clarify @Bean(autowire=NO)
@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 15, 2016

Juergen Hoeller commented

This would be great to have... if we could technically do it. Unfortunately, the body of a factory method is a black box to the framework; we have no idea what's being called there. For that reason, this currently doesn't work. I'd love to make that work though!

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 15, 2016

Christian Hersevoort commented

Thanks for your quick reply!

Does this also mean @Bean(autowire = Autowire.NO) is broken? Because (even) without solving the "black-box" issue of @Configuration, the user can tell Spring the bean is "done" injecting with Autowire.NO.

Or does Autowire.NO mean something else in Spring?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 15, 2016

Juergen Hoeller commented

This "autowire mode" indicates convention-based autowiring, introspecting setter method signatures and autowiring them by name or by type. Unfortunately, this is completely independent from annotation-driven autowiring. Autowire.NO is actually the default on @Bean, indicating no such convention-based autowiring to be performed. That said, I like the idea of a flag which allows for skipping annotation-driven injection. We could introduce a dedicated flag for that or simply repurpose Autowire.NO as you implied.

Note that these problems do not arise with constructor injection. Our general recommendation for @Bean methods interacting with annotated component classes is to favor constructor injection, with annotated constructors naturally ignored on instances returned from @Bean methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core type: enhancement
Projects
None yet
Development

No branches or pull requests

1 participant