Skip to content

Commit 62a3e41

Browse files
committed
Deprecate mutable methods of MethodParameter
Deprecate all mutation methods in `MethodParameter` in favor of factory methods that return a new instance. Existing code that previously relied on mutation has been updated to use the replacement methods. Closes gh-23385
1 parent 89d150d commit 62a3e41

File tree

7 files changed

+187
-68
lines changed

7 files changed

+187
-68
lines changed

spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,12 +57,13 @@ private GenericTypeResolver() {
5757
* @param methodParameter the method parameter specification
5858
* @param implementationClass the class to resolve type variables against
5959
* @return the corresponding generic parameter or return type
60+
* @deprecated since 5.2 in favor of {@code methodParameter.withContainingClass(implementationClass).getParameterType()}
6061
*/
62+
@Deprecated
6163
public static Class<?> resolveParameterType(MethodParameter methodParameter, Class<?> implementationClass) {
6264
Assert.notNull(methodParameter, "MethodParameter must not be null");
6365
Assert.notNull(implementationClass, "Class must not be null");
6466
methodParameter.setContainingClass(implementationClass);
65-
ResolvableType.resolveMethodParameter(methodParameter);
6667
return methodParameter.getParameterType();
6768
}
6869

spring-core/src/main/java/org/springframework/core/MethodParameter.java

Lines changed: 102 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
* @author Andy Clement
5656
* @author Sam Brannen
5757
* @author Sebastien Deleuze
58+
* @author Phillip Webb
5859
* @since 2.0
5960
* @see org.springframework.core.annotation.SynthesizingMethodParameter
6061
*/
@@ -76,6 +77,7 @@ public class MethodParameter {
7677
@Nullable
7778
Map<Integer, Integer> typeIndexesPerLevel;
7879

80+
/** The containing class. Could also be supplied by overriding {@link #getContainingClass()} */
7981
@Nullable
8082
private volatile Class<?> containingClass;
8183

@@ -150,6 +152,24 @@ public MethodParameter(Constructor<?> constructor, int parameterIndex, int nesti
150152
this.nestingLevel = nestingLevel;
151153
}
152154

155+
/**
156+
* Internal constructor used to create a {@link MethodParameter} with a
157+
* containing class already set.
158+
* @param executable the Executable to specify a parameter for
159+
* @param parameterIndex the index of the parameter
160+
* @param containingClass the containing class
161+
* @since 5.2
162+
*/
163+
MethodParameter(Executable executable, int parameterIndex,
164+
@Nullable Class<?> containingClass) {
165+
166+
Assert.notNull(executable, "Executable must not be null");
167+
this.executable = executable;
168+
this.parameterIndex = validateIndex(executable, parameterIndex);
169+
this.nestingLevel = 1;
170+
this.containingClass = containingClass;
171+
}
172+
153173
/**
154174
* Copy constructor, resulting in an independent MethodParameter object
155175
* based on the same metadata and cache state that the original object was in.
@@ -252,15 +272,20 @@ public int getParameterIndex() {
252272
/**
253273
* Increase this parameter's nesting level.
254274
* @see #getNestingLevel()
275+
* @deprecated since 5.2 in favor of {@link #nested(Integer)}
255276
*/
277+
@Deprecated
256278
public void increaseNestingLevel() {
257279
this.nestingLevel++;
258280
}
259281

260282
/**
261283
* Decrease this parameter's nesting level.
262284
* @see #getNestingLevel()
285+
* @deprecated since 5.2 in favor of retaining the original MethodParameter and
286+
* using {@link #nested(Integer)} if nesting is required
263287
*/
288+
@Deprecated
264289
public void decreaseNestingLevel() {
265290
getTypeIndexesPerLevel().remove(this.nestingLevel);
266291
this.nestingLevel--;
@@ -280,11 +305,22 @@ public int getNestingLevel() {
280305
* @param typeIndex the corresponding type index
281306
* (or {@code null} for the default type index)
282307
* @see #getNestingLevel()
308+
* @deprecated since 5.2 in favor of {@link #withTypeIndex}
283309
*/
310+
@Deprecated
284311
public void setTypeIndexForCurrentLevel(int typeIndex) {
285312
getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex);
286313
}
287314

315+
/**
316+
* Return a variant of this {@code MethodParameter} with the type for the current
317+
* level set to the specified value.
318+
* @param typeIndex the new type index
319+
*/
320+
public MethodParameter withTypeIndex(int typeIndex) {
321+
return nested(this.nestingLevel, typeIndex);
322+
}
323+
288324
/**
289325
* Return the type index for the current nesting level.
290326
* @return the corresponding type index, or {@code null}
@@ -319,22 +355,45 @@ private Map<Integer, Integer> getTypeIndexesPerLevel() {
319355

320356
/**
321357
* Return a variant of this {@code MethodParameter} which points to the
322-
* same parameter but one nesting level deeper. This is effectively the
323-
* same as {@link #increaseNestingLevel()}, just with an independent
324-
* {@code MethodParameter} object (e.g. in case of the original being cached).
358+
* same parameter but one nesting level deeper.
325359
* @since 4.3
326360
*/
327361
public MethodParameter nested() {
362+
return nested(null);
363+
}
364+
365+
/**
366+
* Return a variant of this {@code MethodParameter} which points to the
367+
* same parameter but one nesting level deeper.
368+
* @param typeIndex the type index for the new nesting level
369+
* @since 5.2
370+
*/
371+
public MethodParameter nested(@Nullable Integer typeIndex) {
328372
MethodParameter nestedParam = this.nestedMethodParameter;
329-
if (nestedParam != null) {
373+
if (nestedParam != null && typeIndex == null) {
330374
return nestedParam;
331375
}
332-
nestedParam = clone();
333-
nestedParam.nestingLevel = this.nestingLevel + 1;
334-
this.nestedMethodParameter = nestedParam;
376+
nestedParam = nested(this.nestingLevel + 1, typeIndex);
377+
if (typeIndex == null) {
378+
this.nestedMethodParameter = nestedParam;
379+
}
335380
return nestedParam;
336381
}
337382

383+
private MethodParameter nested(int nestingLevel, @Nullable Integer typeIndex) {
384+
MethodParameter copy = clone();
385+
copy.nestingLevel = nestingLevel;
386+
if (this.typeIndexesPerLevel != null) {
387+
copy.typeIndexesPerLevel = new HashMap<>(this.typeIndexesPerLevel);
388+
}
389+
if (typeIndex != null) {
390+
copy.getTypeIndexesPerLevel().put(copy.nestingLevel, typeIndex);
391+
}
392+
copy.parameterType = null;
393+
copy.genericParameterType = null;
394+
return copy;
395+
}
396+
338397
/**
339398
* Return whether this method indicates a parameter which is not required:
340399
* either in the form of Java 8's {@link java.util.Optional}, any variant
@@ -376,21 +435,28 @@ public MethodParameter nestedIfOptional() {
376435
return (getParameterType() == Optional.class ? nested() : this);
377436
}
378437

379-
/**
380-
* Set a containing class to resolve the parameter type against.
381-
*/
438+
public Class<?> getContainingClass() {
439+
Class<?> containingClass = this.containingClass;
440+
return (containingClass != null ? containingClass : getDeclaringClass());
441+
}
442+
443+
@Deprecated
382444
void setContainingClass(Class<?> containingClass) {
383445
this.containingClass = containingClass;
446+
this.parameterType = null;
384447
}
385448

386-
public Class<?> getContainingClass() {
387-
Class<?> containingClass = this.containingClass;
388-
return (containingClass != null ? containingClass : getDeclaringClass());
449+
public MethodParameter withContainingClass(@Nullable Class<?> containingClass) {
450+
MethodParameter result = clone();
451+
result.containingClass = containingClass;
452+
result.parameterType = null;
453+
return result;
389454
}
390455

391456
/**
392457
* Set a resolved (generic) parameter type.
393458
*/
459+
@Deprecated
394460
void setParameterType(@Nullable Class<?> parameterType) {
395461
this.parameterType = parameterType;
396462
}
@@ -401,18 +467,16 @@ void setParameterType(@Nullable Class<?> parameterType) {
401467
*/
402468
public Class<?> getParameterType() {
403469
Class<?> paramType = this.parameterType;
470+
if (paramType != null) {
471+
return paramType;
472+
}
473+
if (getContainingClass() != null) {
474+
paramType = ResolvableType.forMethodParameter(this, null, 1, null).resolve();
475+
}
404476
if (paramType == null) {
405-
if (this.parameterIndex < 0) {
406-
Method method = getMethod();
407-
paramType = (method != null ?
408-
(KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass()) ?
409-
KotlinDelegate.getReturnType(method) : method.getReturnType()) : void.class);
410-
}
411-
else {
412-
paramType = this.executable.getParameterTypes()[this.parameterIndex];
413-
}
414-
this.parameterType = paramType;
477+
paramType = computeParameterType();
415478
}
479+
this.parameterType = paramType;
416480
return paramType;
417481
}
418482

@@ -442,13 +506,27 @@ public Type getGenericParameterType() {
442506
index = this.parameterIndex - 1;
443507
}
444508
paramType = (index >= 0 && index < genericParameterTypes.length ?
445-
genericParameterTypes[index] : getParameterType());
509+
genericParameterTypes[index] : computeParameterType());
446510
}
447511
this.genericParameterType = paramType;
448512
}
449513
return paramType;
450514
}
451515

516+
private Class<?> computeParameterType() {
517+
if (this.parameterIndex < 0) {
518+
Method method = getMethod();
519+
if (method == null) {
520+
return void.class;
521+
}
522+
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass())) {
523+
return KotlinDelegate.getReturnType(method);
524+
}
525+
return method.getReturnType();
526+
}
527+
return this.executable.getParameterTypes()[this.parameterIndex];
528+
}
529+
452530
/**
453531
* Return the nested type of the method/constructor parameter.
454532
* @return the parameter type (never {@code null})
@@ -688,7 +766,6 @@ public MethodParameter clone() {
688766
return new MethodParameter(this);
689767
}
690768

691-
692769
/**
693770
* Create a new MethodParameter for the given method or constructor.
694771
* <p>This is a convenience factory method for scenarios where a

spring-core/src/main/java/org/springframework/core/ResolvableType.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,8 +1200,7 @@ public static ResolvableType forConstructorParameter(Constructor<?> constructor,
12001200
Class<?> implementationClass) {
12011201

12021202
Assert.notNull(constructor, "Constructor must not be null");
1203-
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
1204-
methodParameter.setContainingClass(implementationClass);
1203+
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex, implementationClass);
12051204
return forMethodParameter(methodParameter);
12061205
}
12071206

@@ -1227,8 +1226,7 @@ public static ResolvableType forMethodReturnType(Method method) {
12271226
*/
12281227
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
12291228
Assert.notNull(method, "Method must not be null");
1230-
MethodParameter methodParameter = new MethodParameter(method, -1);
1231-
methodParameter.setContainingClass(implementationClass);
1229+
MethodParameter methodParameter = new MethodParameter(method, -1, implementationClass);
12321230
return forMethodParameter(methodParameter);
12331231
}
12341232

@@ -1258,8 +1256,7 @@ public static ResolvableType forMethodParameter(Method method, int parameterInde
12581256
*/
12591257
public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) {
12601258
Assert.notNull(method, "Method must not be null");
1261-
MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
1262-
methodParameter.setContainingClass(implementationClass);
1259+
MethodParameter methodParameter = new MethodParameter(method, parameterIndex, implementationClass);
12631260
return forMethodParameter(methodParameter);
12641261
}
12651262

@@ -1303,22 +1300,29 @@ public static ResolvableType forMethodParameter(MethodParameter methodParameter,
13031300
*/
13041301
public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) {
13051302
Assert.notNull(methodParameter, "MethodParameter must not be null");
1306-
ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass());
1307-
return forType(targetType, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()).
1308-
getNested(methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
1303+
int nestingLevel = methodParameter.getNestingLevel();
1304+
Map<Integer, Integer> typeIndexesPerLevel = methodParameter.typeIndexesPerLevel;
1305+
return forMethodParameter(methodParameter, targetType, nestingLevel,
1306+
typeIndexesPerLevel);
13091307
}
13101308

13111309
/**
1312-
* Resolve the top-level parameter type of the given {@code MethodParameter}.
1313-
* @param methodParameter the method parameter to resolve
1314-
* @since 4.1.9
1315-
* @see MethodParameter#setParameterType
1310+
* Return a {@link ResolvableType} for the specified {@link MethodParameter} at
1311+
* a specific nesting level, overriding the target type to resolve with a specific
1312+
* given type.
1313+
* @param methodParameter the source method parameter (must not be {@code null})
1314+
* @param targetType the type to resolve (a part of the method parameter's type)
1315+
* @param nestingLevel the nesting level to use
1316+
* @param typeIndexesPerLevel the type indexes per nesting level
1317+
* @return a {@link ResolvableType} for the specified method parameter
1318+
* @since 5.2
1319+
* @see #forMethodParameter(Method, int)
13161320
*/
1317-
static void resolveMethodParameter(MethodParameter methodParameter) {
1318-
Assert.notNull(methodParameter, "MethodParameter must not be null");
1321+
static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType,
1322+
int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) {
13191323
ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass());
1320-
methodParameter.setParameterType(
1321-
forType(null, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()).resolve());
1324+
return forType(targetType, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()).
1325+
getNested(nestingLevel, typeIndexesPerLevel);
13221326
}
13231327

13241328
/**

spring-core/src/main/java/org/springframework/core/convert/Property.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@
2323
import java.util.LinkedHashMap;
2424
import java.util.Map;
2525

26-
import org.springframework.core.GenericTypeResolver;
2726
import org.springframework.core.MethodParameter;
2827
import org.springframework.lang.Nullable;
2928
import org.springframework.util.ConcurrentReferenceHashMap;
@@ -187,21 +186,15 @@ private MethodParameter resolveReadMethodParameter() {
187186
if (getReadMethod() == null) {
188187
return null;
189188
}
190-
return resolveParameterType(new MethodParameter(getReadMethod(), -1));
189+
return new MethodParameter(getReadMethod(), -1).withContainingClass(getObjectType());
191190
}
192191

193192
@Nullable
194193
private MethodParameter resolveWriteMethodParameter() {
195194
if (getWriteMethod() == null) {
196195
return null;
197196
}
198-
return resolveParameterType(new MethodParameter(getWriteMethod(), 0));
199-
}
200-
201-
private MethodParameter resolveParameterType(MethodParameter parameter) {
202-
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
203-
GenericTypeResolver.resolveParameterType(parameter, getObjectType());
204-
return parameter;
197+
return new MethodParameter(getWriteMethod(), 0).withContainingClass(getObjectType());
205198
}
206199

207200
private Annotation[] resolveAnnotations() {

0 commit comments

Comments
 (0)