Consistently use DefaultParameterNameDiscoverer.getSharedInstance()

This includes MethodParameter resolving getParameterName() by default now.
initParameterNameDiscovery(null) can be used to suppress such resolution.

Closes gh-36024
This commit is contained in:
Juergen Hoeller
2025-12-17 13:39:02 +01:00
parent ec6b7730a2
commit c813577908
19 changed files with 61 additions and 41 deletions
@@ -29,7 +29,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.Assert;
/**
@@ -51,8 +50,6 @@ import org.springframework.util.Assert;
*/
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private final ProxyMethodInvocation methodInvocation;
private @Nullable Object @Nullable [] args;
@@ -217,7 +214,7 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
public @Nullable String @Nullable [] getParameterNames() {
@Nullable String[] parameterNames = this.parameterNames;
if (parameterNames == null) {
parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
parameterNames = DefaultParameterNameDiscoverer.getSharedInstance().getParameterNames(getMethod());
this.parameterNames = parameterNames;
}
return parameterNames;
@@ -45,7 +45,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -72,9 +71,6 @@ import org.springframework.util.StringUtils;
*/
public abstract class BeanUtils {
private static final ParameterNameDiscoverer parameterNameDiscoverer =
new DefaultParameterNameDiscoverer();
private static final Set<Class<?>> unknownEditorTypes =
Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64));
@@ -659,7 +655,8 @@ public abstract class BeanUtils {
@SuppressWarnings("NullAway") // Dataflow analysis limitation
public static @Nullable String[] getParameterNames(Constructor<?> ctor) {
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
@Nullable String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
@Nullable String[] paramNames = (cp != null ? cp.value() :
DefaultParameterNameDiscoverer.getSharedInstance().getParameterNames(ctor));
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
int parameterCount = (KOTLIN_REFLECT_PRESENT && KotlinDelegate.hasDefaultConstructorMarker(ctor) ?
ctor.getParameterCount() - 1 : ctor.getParameterCount());
@@ -39,21 +39,24 @@ class CacheEvaluationContextFactory {
private @Nullable Supplier<ParameterNameDiscoverer> parameterNameDiscoverer;
CacheEvaluationContextFactory(StandardEvaluationContext originalContext) {
this.originalContext = originalContext;
}
public void setParameterNameDiscoverer(Supplier<ParameterNameDiscoverer> parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
public ParameterNameDiscoverer getParameterNameDiscoverer() {
if (this.parameterNameDiscoverer == null) {
this.parameterNameDiscoverer = SingletonSupplier.of(new DefaultParameterNameDiscoverer());
this.parameterNameDiscoverer = SingletonSupplier.of(DefaultParameterNameDiscoverer.getSharedInstance());
}
return this.parameterNameDiscoverer.get();
}
/**
* Creates a {@link CacheEvaluationContext} for the specified operation.
* @param rootObject the {@code root} object to use for the context
@@ -38,8 +38,6 @@ public abstract class CachedExpressionEvaluator {
private final SpelExpressionParser parser;
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
/**
* Create a new instance with the default {@link SpelExpressionParser}.
@@ -69,7 +67,7 @@ public abstract class CachedExpressionEvaluator {
* @since 4.3
*/
protected ParameterNameDiscoverer getParameterNameDiscoverer() {
return this.parameterNameDiscoverer;
return DefaultParameterNameDiscoverer.getSharedInstance();
}
/**
@@ -183,7 +183,8 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
private boolean exposeClassDescriptor = false;
private @Nullable ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private @Nullable ParameterNameDiscoverer parameterNameDiscoverer =
DefaultParameterNameDiscoverer.getSharedInstance();
/**
@@ -507,8 +508,8 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
* @return the {@code MBeanParameterInfo} array
*/
protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) {
ParameterNameDiscoverer paramNameDiscoverer = getParameterNameDiscoverer();
@Nullable String[] paramNames = (paramNameDiscoverer != null ? paramNameDiscoverer.getParameterNames(method) : null);
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
@Nullable String[] paramNames = (pnd != null ? pnd.getParameterNames(method) : null);
if (paramNames == null) {
return new MBeanParameterInfo[0];
}
@@ -87,7 +87,7 @@ public class MethodValidationAdapter implements MethodValidator {
private MessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer = DefaultParameterNameDiscoverer.getSharedInstance();
private ObjectNameResolver objectNameResolver = defaultObjectNameResolver;
@@ -16,6 +16,8 @@
package org.springframework.core;
import org.jspecify.annotations.Nullable;
/**
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
* delegating to the Java 8 standard reflection mechanism.
@@ -37,6 +39,8 @@ public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDisc
private static final boolean KOTLIN_REFLECT_PRESENT = KotlinDetector.isKotlinReflectPresent();
private static volatile @Nullable DefaultParameterNameDiscoverer sharedInstance;
public DefaultParameterNameDiscoverer() {
if (KOTLIN_REFLECT_PRESENT) {
@@ -47,4 +51,25 @@ public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDisc
addDiscoverer(new StandardReflectionParameterNameDiscoverer());
}
/**
* Return a shared default {@code ParameterNameDiscoverer} instance,
* lazily building it once needed.
* @return the shared {@code ParameterNameDiscoverer} instance
* @since 7.0.3
*/
public static ParameterNameDiscoverer getSharedInstance() {
DefaultParameterNameDiscoverer pnd = sharedInstance;
if (pnd == null) {
synchronized (DefaultParameterNameDiscoverer.class) {
pnd = sharedInstance;
if (pnd == null) {
pnd = new DefaultParameterNameDiscoverer();
sharedInstance = pnd;
}
}
}
return pnd;
}
}
@@ -89,7 +89,8 @@ public class MethodParameter {
private volatile Annotation @Nullable [] parameterAnnotations;
private volatile @Nullable ParameterNameDiscoverer parameterNameDiscoverer;
private volatile @Nullable ParameterNameDiscoverer parameterNameDiscoverer =
DefaultParameterNameDiscoverer.getSharedInstance();
volatile @Nullable String parameterName;
@@ -667,6 +668,10 @@ public class MethodParameter {
* <p>This method does not actually try to retrieve the parameter name at
* this point; it just allows discovery to happen when the application calls
* {@link #getParameterName()} (if ever).
* <p>Note: As of 7.0.3, a default parameter name discoverer is available.
* This init method can be used to override the default discoverer or to
* suppress discovery (passing {@code null}).
* @see DefaultParameterNameDiscoverer#getSharedInstance()
*/
public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
@@ -252,6 +252,12 @@ class MethodParameterTests {
assertThat(m3.getTypeIndexForCurrentLevel()).isEqualTo(3);
}
@Test
void parameterNames() {
assertThat(stringParameter.getParameterName()).isEqualTo("str");
assertThat(longParameter.getParameterName()).isEqualTo("lng");
}
@Test
void jspecifyNullableParameter() {
assertThat(jspecifyNullableParameter.isOptional()).isTrue();
@@ -272,7 +278,8 @@ class MethodParameterTests {
assertThat(springNonNullParameter.isOptional()).isFalse();
}
public int method(String p1, long p2) {
public int method(String str, long lng) {
return 42;
}
@@ -117,21 +117,18 @@ class MethodParameterKotlinTests {
@Test
fun `Parameter name for regular function`() {
val methodParameter = returnMethodParameter("nullable", 0)
methodParameter.initParameterNameDiscovery(KotlinReflectionParameterNameDiscoverer())
assertThat(methodParameter.getParameterName()).isEqualTo("nullable")
}
@Test
fun `Parameter name for suspending function`() {
val methodParameter = returnMethodParameter("suspendFun", 0)
methodParameter.initParameterNameDiscovery(KotlinReflectionParameterNameDiscoverer())
assertThat(methodParameter.getParameterName()).isEqualTo("p1")
}
@Test
fun `Continuation parameter name for suspending function`() {
val methodParameter = returnMethodParameter("suspendFun", 1)
methodParameter.initParameterNameDiscovery(KotlinReflectionParameterNameDiscoverer())
assertThat(methodParameter.getParameterName()).isNull()
}
@@ -47,7 +47,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer = DefaultParameterNameDiscoverer.getSharedInstance();
/**
@@ -55,7 +55,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
private final HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer = DefaultParameterNameDiscoverer.getSharedInstance();
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
@@ -30,7 +30,6 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterizedTypeReference;
@@ -90,11 +89,9 @@ final class RSocketServiceMethod {
count -= 1;
}
DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
MethodParameter[] parameters = new MethodParameter[count];
for (int i = 0; i < count; i++) {
parameters[i] = new SynthesizingMethodParameter(method, i);
parameters[i].initParameterNameDiscovery(nameDiscoverer);
}
return parameters;
}
@@ -71,7 +71,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer = DefaultParameterNameDiscoverer.getSharedInstance();
private @Nullable WebDataBinderFactory dataBinderFactory;
@@ -30,7 +30,6 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterizedTypeReference;
@@ -115,11 +114,9 @@ final class HttpServiceMethod {
count -= 1;
}
DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
MethodParameter[] parameters = new MethodParameter[count];
for (int i = 0; i < count; i++) {
parameters[i] = new SynthesizingMethodParameter(method, i);
parameters[i].initParameterNameDiscovery(nameDiscoverer);
}
return parameters;
}
@@ -90,7 +90,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
private final HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer = DefaultParameterNameDiscoverer.getSharedInstance();
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
@@ -605,14 +605,15 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
Set<String> directPaths = getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
HandlerMethodMappingNamingStrategy<T> namingStrategy = getNamingStrategy();
if (namingStrategy != null) {
name = namingStrategy.getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
@@ -40,10 +40,8 @@ import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotatedMethod;
import org.springframework.objenesis.ObjenesisException;
@@ -112,8 +110,6 @@ public class MvcUriComponentsBuilder {
private static final PathMatcher pathMatcher = new AntPathMatcher();
private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private static final CompositeUriComponentsContributor defaultUriComponentsContributor;
static {
@@ -608,7 +604,6 @@ public class MvcUriComponentsBuilder {
final Map<String, Object> uriVars = new HashMap<>();
for (int i = 0; i < paramCount; i++) {
MethodParameter param = annotatedMethod.getMethodParameters()[i];
param.initParameterNameDiscovery(parameterNameDiscoverer);
contributor.contributeMethodArgument(param, args[i], builder, uriVars);
}
@@ -187,7 +187,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer = DefaultParameterNameDiscoverer.getSharedInstance();
private @Nullable ConfigurableBeanFactory beanFactory;