Skip to content

Commit 79d7a28

Browse files
onobcmp911de
authored andcommitted
Stop supporting params not annotated with @ProjectedPayload.
This commit drops support for projections if a parameter (or the parameter type) is not explicitly annotated with `@ProjectedPayload`. A warning is still logged in these cases to help user understand why the parameter is not being projected. Also, simplify tests. Closes #3301 Original pull request: #3453 Related ticket: #3298
1 parent f420152 commit 79d7a28

File tree

3 files changed

+60
-24
lines changed

3 files changed

+60
-24
lines changed

src/main/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolver.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public boolean supportsParameter(MethodParameter parameter) {
115115
// Parameter annotated with @ModelAttribute
116116
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
117117
this.deprecationLogger.logDeprecationForParameter(parameter);
118-
return true;
118+
return false;
119119
}
120120

121121
// Exclude any other parameters annotated with Spring annotation
@@ -131,7 +131,7 @@ public boolean supportsParameter(MethodParameter parameter) {
131131
String packageName = ClassUtils.getPackageName(type);
132132
if (IGNORED_PACKAGES.stream().noneMatch(packageName::startsWith)) {
133133
this.deprecationLogger.logDeprecationForParameter(parameter);
134-
return true;
134+
return false;
135135
}
136136

137137
return false;
@@ -157,7 +157,7 @@ protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest requ
157157
*/
158158
static class ProjectedPayloadDeprecationLogger {
159159

160-
private static final String MESSAGE = "Parameter %sat index %s in [%s] is not annotated with @ProjectedPayload. Support for parameters not annotated with @ProjectedPayload (at the parameter or type level) will be dropped in a future version.";
160+
private static final String MESSAGE = "Parameter %sat index %s in [%s] is not annotated with @ProjectedPayload. Make sure to annotate it with @ProjectedPayload (at the parameter or type level) to use it for projections.";
161161

162162
private final Set<MethodParameter> loggedParameters = Collections.synchronizedSet(new HashSet<>());
163163

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2026-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* @author Chris Bono
19+
* @see org.springframework.data.web.ProxyingHandlerMethodArgumentResolverUnitTests
20+
*/
21+
package example;
22+
23+
import org.springframework.data.web.ProjectedPayload;
24+
25+
@ProjectedPayload
26+
public interface ProjectedPayloadMarkedSampleInterface {}

src/test/java/org/springframework/data/web/ProxyingHandlerMethodArgumentResolverUnitTests.java

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.*;
1919
import static org.mockito.Mockito.*;
2020

21+
import example.ProjectedPayloadMarkedSampleInterface;
2122
import example.SampleInterface;
2223

2324
import java.lang.reflect.Method;
@@ -52,25 +53,33 @@ class ProxyingHandlerMethodArgumentResolverUnitTests {
5253
() -> new DefaultConversionService(), true);
5354

5455
@Test // DATACMNS-776
55-
void supportAnnotatedInterface() {
56+
void supportsAnnotatedInterfaceFromSpringNamespace() {
5657

57-
var parameter = getParameter("with", AnnotatedInterface.class);
58+
var parameter = getParameter("withSpringAnnotatedInterface");
5859

5960
assertThat(resolver.supportsParameter(parameter)).isTrue();
6061
}
6162

6263
@Test // DATACMNS-776
63-
void supportsUnannotatedInterfaceFromUserPackage() {
64+
void doesNotSupportUnannotatedInterfaceFromSpringNamespace() {
65+
66+
var parameter = getParameter("withSpringUnannotatedInterface");
67+
68+
assertThat(resolver.supportsParameter(parameter)).isFalse();
69+
}
6470

65-
var parameter = getParameter("with", SampleInterface.class);
71+
@Test // GH-3301
72+
void supportsAnnotatedInterfaceFromUserPackage() {
73+
74+
var parameter = getParameter("withUserAnnotatedInterface");
6675

6776
assertThat(resolver.supportsParameter(parameter)).isTrue();
6877
}
6978

70-
@Test // DATACMNS-776
71-
void doesNotSupportUnannotatedInterfaceFromSpringNamespace() {
79+
@Test // GH-3301
80+
void doesNotSupportUnannotatedInterfaceFromUserPackage() {
7281

73-
var parameter = getParameter("with", UnannotatedInterface.class);
82+
var parameter = getParameter("withUserUnannotatedInterface");
7483

7584
assertThat(resolver.supportsParameter(parameter)).isFalse();
7685
}
@@ -91,12 +100,12 @@ void doesNotSupportForeignSpringAnnotations() {
91100
assertThat(resolver.supportsParameter(parameter)).isFalse();
92101
}
93102

94-
@Test // GH-2937
95-
void doesSupportAtModelAttribute() {
103+
@Test // GH-3301
104+
void doesNotSupportAtModelAttribute() {
96105

97106
var parameter = getParameter("withModelAttribute", SampleInterface.class);
98107

99-
assertThat(resolver.supportsParameter(parameter)).isTrue();
108+
assertThat(resolver.supportsParameter(parameter)).isFalse();
100109
}
101110

102111
@Test // GH-3258
@@ -123,26 +132,27 @@ void doesNotSupportAtProjectedPayloadForMultipartParam() {
123132
assertThat(resolver.supportsParameter(parameter)).isFalse();
124133
}
125134

126-
@Test // GH-3300
135+
@ParameterizedTest // GH-3300
136+
@ValueSource(strings = { "withModelAttribute", "withUserUnannotatedInterface" })
127137
@SuppressWarnings("unchecked")
128-
void deprecationLoggerOnlyLogsOncePerParameter() {
138+
void deprecationLoggerOnlyLogsOncePerParameter(String methodName) {
129139

130-
var parameter = getParameter("withModelAttribute", SampleInterface.class);
140+
var parameter = getParameter(methodName);
131141

132142
// Spy on the actual logger
133143
var actualLoggerSpy = spy(new LogAccessor(ProxyingHandlerMethodArgumentResolver.class));
134144
ReflectionTestUtils.setField(ProxyingHandlerMethodArgumentResolver.class, "LOGGER", actualLoggerSpy,
135145
LogAccessor.class);
136146

137147
// Invoke twice but should only log the first time
138-
assertThat(resolver.supportsParameter(parameter)).isTrue();
148+
assertThat(resolver.supportsParameter(parameter)).isFalse();
139149
verify(actualLoggerSpy, times(1)).warn(any(Supplier.class));
140-
assertThat(resolver.supportsParameter(parameter)).isTrue();
150+
assertThat(resolver.supportsParameter(parameter)).isFalse();
141151
verifyNoMoreInteractions(actualLoggerSpy);
142152
}
143153

144154
@ParameterizedTest // GH-3300
145-
@ValueSource(strings = { "withProjectedPayload", "withAnnotatedInterface" })
155+
@ValueSource(strings = { "withProjectedPayload", "withSpringAnnotatedInterface", "withUserAnnotatedInterface" })
146156
void shouldNotLogDeprecationForValidUsage(String methodName) {
147157

148158
var parameter = getParameter(methodName);
@@ -152,7 +162,7 @@ void shouldNotLogDeprecationForValidUsage(String methodName) {
152162
ReflectionTestUtils.setField(ProxyingHandlerMethodArgumentResolver.class, "LOGGER", actualLoggerSpy,
153163
LogAccessor.class);
154164

155-
// Invoke twice but should only log the first time
165+
// Invoke should not log
156166
assertThat(resolver.supportsParameter(parameter)).isTrue();
157167
verifyNoInteractions(actualLoggerSpy);
158168
}
@@ -182,13 +192,13 @@ interface UnannotatedInterface {}
182192

183193
interface Controller {
184194

185-
void with(AnnotatedInterface param);
195+
void withSpringAnnotatedInterface(AnnotatedInterface param);
186196

187-
void withAnnotatedInterface(@ModelAttribute AnnotatedInterface param);
197+
void withSpringUnannotatedInterface(UnannotatedInterface param);
188198

189-
void with(UnannotatedInterface param);
199+
void withUserAnnotatedInterface(ProjectedPayloadMarkedSampleInterface param);
190200

191-
void with(SampleInterface param);
201+
void withUserUnannotatedInterface(SampleInterface param);
192202

193203
void with(List<Object> param);
194204

0 commit comments

Comments
 (0)