-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Description
Sam Brannen opened SPR-14056 and commented
Status Quo
Spring supports autowiring of dependencies into fields, constructors, and methods; however, there is currently no first-class support for autowiring an individual parameter in a method or constructor.
Spring 5.0's JUnit 5 support demonstrates that it is already possible to implement such a feature with zero changes to core Spring; however, it would be very helpful if the necessary code were hidden behind first-class methods in Spring (see the isAutowirable and resolveDependency methods in ParameterAutowireUtils).
Impetus for Change
For traditional dependency injection use cases, there is no need to inject an individual parameter; however, JUnit 5 introduces a ParameterResolver extension API (analogous to Spring MVC's HandlerMethodArgumentResolver) that allows multiple, competing extension providers to inject values into constructors or test methods. In such a scenario, Spring is not the sole entity performing injection, and Spring therefore cannot simply perform injection for the entire constructor or method (as would be the case for an @Autowired method). Rather, the Spring TestContext Framework (TCF) needs a mechanism for resolving dependencies for individual method parameters (preferably via the java.lang.reflect.Parameter API).
Although the primary impetus is proper JUnit 5 support in the TCF, such a feature could be applied to MVC handler methods, JMS listener methods, etc.
Working Solution
The following excerpt from org.springframework.test.context.junit.jupiter.ParameterAutowireUtils demonstrates how such a feature can be implemented.
static boolean isAutowirable(Parameter parameter) {
return ApplicationContext.class.isAssignableFrom(parameter.getType())
|| AnnotatedElementUtils.hasAnnotation(parameter, Autowired.class)
|| AnnotatedElementUtils.hasAnnotation(parameter, Qualifier.class)
|| AnnotatedElementUtils.hasAnnotation(parameter, Value.class);
}
static Object resolveDependency(Parameter parameter, Class<?> containingClass, ApplicationContext applicationContext) {
boolean required = findMergedAnnotation(parameter, Autowired.class).map(Autowired::required).orElse(true);
MethodParameter methodParameter = SynthesizingMethodParameter.forParameter(parameter);
DependencyDescriptor descriptor = new DependencyDescriptor(methodParameter, required);
descriptor.setContainingClass(containingClass);
return applicationContext.getAutowireCapableBeanFactory().resolveDependency(descriptor, null);
}
private static <A extends Annotation> Optional<A> findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
return Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(element, annotationType));
}Deliverables
- Decide if Spring should support autowiring of individual method parameters as a first-class citizen alongside the existing support for fields, methods, and constructors.
Issue Links:
- Allow @Autowired to be declared on parameters [SPR-14057] #18629 Allow
@Autowiredto be declared on parameters ("depends on") - Compose @Autowired with @Qualifier [SPR-14060] #18632 Compose
@Autowiredwith@Qualifier - Introduce support for JUnit 5 in the TestContext framework [SPR-13575] #18151 Introduce support for JUnit 5 in the TestContext framework
- Allow @Qualifier to be used in composed annotations with attribute overrides [SPR-14058] #18630 Allow
@Qualifierto be used in composed annotations with attribute overrides - Honor @Autowired(required=false) at parameter level, as an alternative to java.util.Optional [SPR-15268] #19833 Honor
@Autowired(required=false) at parameter level, as an alternative to java.util.Optional - Open parameter autowiring utility for external use [SPR-17627] #19926 Open test parameter autowiring utility for external use
Referenced from: commits d7fe92d, 35eb52e
3 votes, 7 watchers