mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Introduce support for custom parameter names in ParameterResolutionDelegate
The resolveDependency() utility method in ParameterResolutionDelegate
resolves a dependency using the name of the parameter as a fallback
qualifier. That suffices for most use cases; however, there are times
when a custom parameter name should be used instead.
For example, for our Bean Override support in the Spring TestContext
Framework, an annotation such as @MockitoBean("myBean") specifies an
explicit name that should be used instead of name of the annotated
parameter.
Furthermore, introducing support for custom parameter names will
greatly simplify the logic in SpringExtension that will be required to
implement #36096.
To address those issues, this commit introduces an overloaded variant
of resolveDependency() which accepts a custom parameter name.
Internally, a custom DependencyDescriptor has been implemented to
transparently support this use case.
See gh-36096
Closes gh-36534
This commit is contained in:
+63
-13
@@ -92,16 +92,8 @@ public final class ParameterResolutionDelegate {
|
||||
/**
|
||||
* Resolve the dependency for the supplied {@link Parameter} from the
|
||||
* supplied {@link AutowireCapableBeanFactory}.
|
||||
* <p>Provides comprehensive autowiring support for individual method parameters
|
||||
* on par with Spring's dependency injection facilities for autowired fields and
|
||||
* methods, including support for {@link Autowired @Autowired},
|
||||
* {@link Qualifier @Qualifier}, and {@link Value @Value} with support for property
|
||||
* placeholders and SpEL expressions in {@code @Value} declarations.
|
||||
* <p>The dependency is required unless the parameter is annotated or meta-annotated
|
||||
* with {@link Autowired @Autowired} with the {@link Autowired#required required}
|
||||
* flag set to {@code false}.
|
||||
* <p>If an explicit <em>qualifier</em> is not declared, the name of the parameter
|
||||
* will be used as the qualifier for resolving ambiguities.
|
||||
* <p>See {@link #resolveDependency(Parameter, int, String, Class, AutowireCapableBeanFactory)}
|
||||
* for details.
|
||||
* @param parameter the parameter whose dependency should be resolved (must not be
|
||||
* {@code null})
|
||||
* @param parameterIndex the index of the parameter in the constructor or method
|
||||
@@ -113,13 +105,49 @@ public final class ParameterResolutionDelegate {
|
||||
* the dependency (must not be {@code null})
|
||||
* @return the resolved object, or {@code null} if none found
|
||||
* @throws BeansException if dependency resolution failed
|
||||
* @see #resolveDependency(Parameter, int, String, Class, AutowireCapableBeanFactory)
|
||||
*/
|
||||
public static @Nullable Object resolveDependency(
|
||||
Parameter parameter, int parameterIndex, Class<?> containingClass, AutowireCapableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
|
||||
return resolveDependency(parameter, parameterIndex, null, containingClass, beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the dependency for the supplied {@link Parameter} from the
|
||||
* supplied {@link AutowireCapableBeanFactory}.
|
||||
* <p>Provides comprehensive autowiring support for individual method parameters
|
||||
* on par with Spring's dependency injection facilities for autowired fields and
|
||||
* methods, including support for {@link Autowired @Autowired},
|
||||
* {@link Qualifier @Qualifier}, and {@link Value @Value} with support for property
|
||||
* placeholders and SpEL expressions in {@code @Value} declarations.
|
||||
* <p>The dependency is required unless the parameter is annotated or meta-annotated
|
||||
* with {@link Autowired @Autowired} with the {@link Autowired#required required}
|
||||
* flag set to {@code false}.
|
||||
* <p>If an explicit <em>qualifier</em> is not declared, the name of the parameter
|
||||
* (or a supplied custom name) will be used as the qualifier for resolving ambiguities.
|
||||
* @param parameter the parameter whose dependency should be resolved (must not be
|
||||
* {@code null})
|
||||
* @param parameterIndex the index of the parameter in the constructor or method
|
||||
* that declares the parameter
|
||||
* @param parameterName a custom name for the parameter; or {@code null} to use
|
||||
* the default parameter name discovery logic
|
||||
* @param containingClass the concrete class that contains the parameter; this may
|
||||
* differ from the class that declares the parameter in that it may be a subclass
|
||||
* thereof, potentially substituting type variables (must not be {@code null})
|
||||
* @param beanFactory the {@code AutowireCapableBeanFactory} from which to resolve
|
||||
* the dependency (must not be {@code null})
|
||||
* @return the resolved object, or {@code null} if none found
|
||||
* @throws BeansException if dependency resolution failed
|
||||
* @since 7.1
|
||||
* @see #isAutowirable
|
||||
* @see Autowired#required
|
||||
* @see SynthesizingMethodParameter#forExecutable(Executable, int)
|
||||
* @see AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)
|
||||
*/
|
||||
public static @Nullable Object resolveDependency(
|
||||
Parameter parameter, int parameterIndex, Class<?> containingClass, AutowireCapableBeanFactory beanFactory)
|
||||
public static @Nullable Object resolveDependency(Parameter parameter, int parameterIndex,
|
||||
@Nullable String parameterName, Class<?> containingClass, AutowireCapableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
|
||||
Assert.notNull(parameter, "Parameter must not be null");
|
||||
@@ -132,7 +160,7 @@ public final class ParameterResolutionDelegate {
|
||||
|
||||
MethodParameter methodParameter = SynthesizingMethodParameter.forExecutable(
|
||||
parameter.getDeclaringExecutable(), parameterIndex);
|
||||
DependencyDescriptor descriptor = new DependencyDescriptor(methodParameter, required);
|
||||
DependencyDescriptor descriptor = new NamedParameterDependencyDescriptor(methodParameter, required, parameterName);
|
||||
descriptor.setContainingClass(containingClass);
|
||||
return beanFactory.resolveDependency(descriptor, null);
|
||||
}
|
||||
@@ -171,4 +199,26 @@ public final class ParameterResolutionDelegate {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class NamedParameterDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
private final @Nullable String parameterName;
|
||||
|
||||
NamedParameterDependencyDescriptor(MethodParameter methodParameter, boolean required, @Nullable String parameterName) {
|
||||
super(methodParameter, required);
|
||||
this.parameterName = parameterName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getDependencyName() {
|
||||
return (this.parameterName != null ? this.parameterName : super.getDependencyName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesStandardBeanLookup() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+55
@@ -134,9 +134,64 @@ class ParameterResolutionTests {
|
||||
parameter, parameterIndex, AutowirableClass.class, beanFactory);
|
||||
assertThat(intermediateDependencyDescriptor.getAnnotatedElement()).isEqualTo(constructor);
|
||||
assertThat(intermediateDependencyDescriptor.getMethodParameter().getParameter()).isEqualTo(parameter);
|
||||
assertThat(intermediateDependencyDescriptor.usesStandardBeanLookup()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyWithCustomParameterNamePreconditionsForParameter() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ParameterResolutionDelegate.resolveDependency(null, 0, "customName", getClass(), mock()))
|
||||
.withMessageContaining("Parameter must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyWithCustomParameterNamePreconditionsForContainingClass() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ParameterResolutionDelegate.resolveDependency(getParameter(), 0, "customName", null, mock()))
|
||||
.withMessageContaining("Containing class must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyWithCustomParameterNamePreconditionsForBeanFactory() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ParameterResolutionDelegate.resolveDependency(getParameter(), 0, "customName", getClass(), null))
|
||||
.withMessageContaining("AutowireCapableBeanFactory must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyWithNullCustomParameterNameFallsBackToDefaultParameterNameDiscovery() throws Exception {
|
||||
Constructor<?> constructor = AutowirableClass.class.getConstructor(String.class, String.class, String.class, String.class);
|
||||
AutowireCapableBeanFactory beanFactory = mock();
|
||||
given(beanFactory.resolveDependency(any(), isNull())).willAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Parameter[] parameters = constructor.getParameters();
|
||||
for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
|
||||
Parameter parameter = parameters[parameterIndex];
|
||||
DependencyDescriptor via4ArgMethod = (DependencyDescriptor) ParameterResolutionDelegate.resolveDependency(
|
||||
parameter, parameterIndex, AutowirableClass.class, beanFactory);
|
||||
DependencyDescriptor via5ArgMethod = (DependencyDescriptor) ParameterResolutionDelegate.resolveDependency(
|
||||
parameter, parameterIndex, null, AutowirableClass.class, beanFactory);
|
||||
assertThat(via5ArgMethod.getDependencyName()).isEqualTo(via4ArgMethod.getDependencyName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyWithCustomParameterName() throws Exception {
|
||||
Constructor<?> constructor = AutowirableClass.class.getConstructor(String.class, String.class, String.class, String.class);
|
||||
AutowireCapableBeanFactory beanFactory = mock();
|
||||
given(beanFactory.resolveDependency(any(), isNull())).willAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
Parameter parameter = constructor.getParameters()[0];
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) ParameterResolutionDelegate.resolveDependency(
|
||||
parameter, 0, "customBeanName", AutowirableClass.class, beanFactory);
|
||||
|
||||
assertThat(descriptor.getAnnotatedElement()).isEqualTo(constructor);
|
||||
assertThat(descriptor.getMethodParameter().getParameter()).isEqualTo(parameter);
|
||||
assertThat(descriptor.getDependencyName()).isEqualTo("customBeanName");
|
||||
assertThat(descriptor.usesStandardBeanLookup()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
void autowirableMethod(
|
||||
@Autowired String firstParameter,
|
||||
|
||||
Reference in New Issue
Block a user