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

@Lazy injection point target is proxied twice for @Resource not matching bean name #28176

Closed
sbrannen opened this issue Mar 13, 2022 · 1 comment
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: declined A suggestion or change that we don't feel we should currently apply

Comments

@sbrannen
Copy link
Member

sbrannen commented Mar 13, 2022

Overview

If the field name for a @Lazy @Resource does not match the bean name of the desired dependency, both CommonAnnotationBeanPostProcessor.buildLazyResourceProxy(...) and ContextAnnotationAutowireCandidateResolver.buildLazyResolutionProxy(...) will create proxies for the injected bean, with the latter creating a proxy that wraps the proxy created by the former.

For example, in the following test case, the fieldNameMatchesBeanName() test passes; whereas, the fieldNameDoesNotMatchBeanName() test fails with an error similar to the following.

Expected size: 2 but was: 3 in:
[example.LazyResourceTests$MyBean$$EnhancerBySpringCGLIB$$c2407600,
    example.LazyResourceTests$MyBean$$EnhancerBySpringCGLIB$$c2407600,
    example.LazyResourceTests.MyBean]
@SpringJUnitConfig
class LazyResourceTests {

	@Lazy
	@Resource
	MyBean myBean;

	@Lazy
	@Resource
	MyBean bean;

	@Test
	void fieldNameMatchesBeanName() throws Exception {
		assertPathToTarget(this.myBean);
	}

	@Test
	void fieldNameDoesNotMatchBeanName() throws Exception {
		assertPathToTarget(this.bean);
	}

	private static void assertPathToTarget(Object beanToInspect) throws Exception {
		List<Class<?>> classes = new ArrayList<>();
		Object current = beanToInspect;
		while (current != null) {
			classes.add(current.getClass());
			if (AopUtils.isAopProxy(current) && current instanceof Advised) {
				current = ((Advised) current).getTargetSource().getTarget();
			}
			else {
				break;
			}
		}
		assertThat(classes).hasSize(2);
	}

	@Configuration
	static class Config {
		@Bean
		MyBean myBean() {
			return new MyBean();
		}
	}

	static class MyBean {
	}

}

Related Issues

@sbrannen sbrannen added in: core Issues in core modules (aop, beans, core, context, expression) status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 13, 2022
@sbrannen sbrannen added this to the Triage Queue milestone Mar 13, 2022
@sbrannen
Copy link
Member Author

Although the creation of two proxies for the same lazy injection point is unintentional, the team has decided to close this issue on the following grounds.

The only way for two such proxies to be created is if a @Lazy @Resource field has a name that does not match the name of a bean in the ApplicationContext, which should not be the case in practice.

When using @Resource, the developer is requesting that Spring perform dependency injection by name. When the field name does not match an existing bean name, Spring's dependency resolution mechanism falls back to dependency injection by type.

Thus, if a user wishes to use @Lazy with dependency injection by type, the injection point should be annotated with @Autowired or @Inject instead of @Resource. Otherwise, the user should ensure the @Resource field name matches an existing bean name or accept the fact that two lazy proxies are created for such scenarios.

@sbrannen sbrannen removed this from the Triage Queue milestone Mar 15, 2022
@sbrannen sbrannen added status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 15, 2022
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: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

1 participant