Skip to content

Commit d045f44

Browse files
committed
Polishing in RequestMappingHandlerMapping
1 parent 7cc29d2 commit d045f44

File tree

2 files changed

+117
-91
lines changed

2 files changed

+117
-91
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -178,27 +178,25 @@ protected boolean isHandler(Class<?> beanType) {
178178
@Override
179179
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
180180
if (this.apiVersionStrategy != null) {
181-
Comparable<?> requestVersion = exchange.getAttribute(API_VERSION_ATTRIBUTE);
182-
if (requestVersion == null) {
183-
requestVersion = getApiVersion(exchange, this.apiVersionStrategy);
184-
if (requestVersion != null) {
185-
exchange.getAttributes().put(API_VERSION_ATTRIBUTE, requestVersion);
181+
Comparable<?> version = exchange.getAttribute(API_VERSION_ATTRIBUTE);
182+
if (version == null) {
183+
version = getApiVersion(exchange, this.apiVersionStrategy);
184+
if (version != null) {
185+
exchange.getAttributes().put(API_VERSION_ATTRIBUTE, version);
186186
}
187187
}
188188
}
189189
return super.getHandlerInternal(exchange);
190190
}
191191

192-
private static @Nullable Comparable<?> getApiVersion(
193-
ServerWebExchange exchange, ApiVersionStrategy versionStrategy) {
194-
195-
String value = versionStrategy.resolveVersion(exchange);
192+
private static @Nullable Comparable<?> getApiVersion(ServerWebExchange exchange, ApiVersionStrategy strategy) {
193+
String value = strategy.resolveVersion(exchange);
196194
if (value == null) {
197-
return versionStrategy.getDefaultVersion();
195+
return strategy.getDefaultVersion();
198196
}
199197
try {
200-
Comparable<?> version = versionStrategy.parseVersion(value);
201-
versionStrategy.validateVersion(version, exchange);
198+
Comparable<?> version = strategy.parseVersion(value);
199+
strategy.validateVersion(version, exchange);
202200
return version;
203201
}
204202
catch (Exception ex) {
@@ -242,42 +240,43 @@ public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
242240
}
243241

244242
private @Nullable RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
245-
RequestMappingInfo requestMappingInfo = null;
243+
244+
List<AnnotationDescriptor> descriptors =
245+
MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()).stream()
246+
.filter(MergedAnnotationPredicates.typeIn(RequestMapping.class, HttpExchange.class))
247+
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
248+
.map(AnnotationDescriptor::new)
249+
.distinct()
250+
.toList();
251+
252+
RequestMappingInfo info = null;
246253
RequestCondition<?> customCondition = (element instanceof Class<?> clazz ?
247254
getCustomTypeCondition(clazz) : getCustomMethodCondition((Method) element));
248255

249-
List<AnnotationDescriptor> descriptors = getAnnotationDescriptors(element);
256+
List<AnnotationDescriptor> mappingDescriptors =
257+
descriptors.stream().filter(desc -> desc.annotation instanceof RequestMapping).toList();
250258

251-
List<AnnotationDescriptor> requestMappings = descriptors.stream()
252-
.filter(desc -> desc.annotation instanceof RequestMapping).toList();
253-
if (!requestMappings.isEmpty()) {
254-
if (requestMappings.size() > 1 && logger.isWarnEnabled()) {
255-
logger.warn("Multiple @RequestMapping annotations found on %s, but only the first will be used: %s"
256-
.formatted(element, requestMappings));
257-
}
258-
requestMappingInfo = createRequestMappingInfo((RequestMapping) requestMappings.get(0).annotation, customCondition);
259+
if (!mappingDescriptors.isEmpty()) {
260+
checkMultipleAnnotations(element, mappingDescriptors);
261+
info = createRequestMappingInfo((RequestMapping) mappingDescriptors.get(0).annotation, customCondition);
259262
}
260263

261-
List<AnnotationDescriptor> httpExchanges = descriptors.stream()
262-
.filter(desc -> desc.annotation instanceof HttpExchange).toList();
263-
if (!httpExchanges.isEmpty()) {
264-
Assert.state(requestMappingInfo == null,
265-
() -> "%s is annotated with @RequestMapping and @HttpExchange annotations, but only one is allowed: %s"
266-
.formatted(element, Stream.of(requestMappings, httpExchanges).flatMap(List::stream).toList()));
267-
Assert.state(httpExchanges.size() == 1,
268-
() -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
269-
.formatted(element, httpExchanges));
270-
requestMappingInfo = createRequestMappingInfo((HttpExchange) httpExchanges.get(0).annotation, customCondition);
264+
List<AnnotationDescriptor> exchangeDescriptors =
265+
descriptors.stream().filter(desc -> desc.annotation instanceof HttpExchange).toList();
266+
267+
if (!exchangeDescriptors.isEmpty()) {
268+
checkMultipleAnnotations(element, info, mappingDescriptors, exchangeDescriptors);
269+
info = createRequestMappingInfo((HttpExchange) exchangeDescriptors.get(0).annotation, customCondition);
271270
}
272271

273-
if (requestMappingInfo != null && this.apiVersionStrategy instanceof DefaultApiVersionStrategy davs) {
274-
String version = requestMappingInfo.getVersionCondition().getVersion();
272+
if (info != null && this.apiVersionStrategy instanceof DefaultApiVersionStrategy davs) {
273+
String version = info.getVersionCondition().getVersion();
275274
if (version != null) {
276275
davs.addMappedVersion(version);
277276
}
278277
}
279278

280-
return requestMappingInfo;
279+
return info;
281280
}
282281

283282
/**
@@ -316,6 +315,28 @@ public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
316315
return null;
317316
}
318317

318+
private void checkMultipleAnnotations(
319+
AnnotatedElement element, List<AnnotationDescriptor> mappingDescriptors) {
320+
321+
if (logger.isWarnEnabled() && mappingDescriptors.size() > 1) {
322+
logger.warn("Multiple @RequestMapping annotations found on %s, but only the first will be used: %s"
323+
.formatted(element, mappingDescriptors));
324+
}
325+
}
326+
327+
private static void checkMultipleAnnotations(
328+
AnnotatedElement element, @Nullable RequestMappingInfo info,
329+
List<AnnotationDescriptor> mappingDescriptors, List<AnnotationDescriptor> exchangeDescriptors) {
330+
331+
Assert.state(info == null,
332+
() -> "%s is annotated with @RequestMapping and @HttpExchange annotations, but only one is allowed: %s"
333+
.formatted(element, Stream.of(mappingDescriptors, exchangeDescriptors).flatMap(List::stream).toList()));
334+
335+
Assert.state(exchangeDescriptors.size() == 1,
336+
() -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
337+
.formatted(element, exchangeDescriptors));
338+
}
339+
319340
/**
320341
* Create a {@link RequestMappingInfo} from the supplied
321342
* {@link RequestMapping @RequestMapping} annotation, meta-annotation,
@@ -510,24 +531,16 @@ private String resolveCorsAnnotationValue(String value) {
510531
}
511532
}
512533

513-
private static List<AnnotationDescriptor> getAnnotationDescriptors(AnnotatedElement element) {
514-
return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none())
515-
.stream()
516-
.filter(MergedAnnotationPredicates.typeIn(RequestMapping.class, HttpExchange.class))
517-
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
518-
.map(AnnotationDescriptor::new)
519-
.distinct()
520-
.toList();
521-
}
522534

523535
private static class AnnotationDescriptor {
524536

525537
private final Annotation annotation;
538+
526539
private final MergedAnnotation<?> root;
527540

528-
AnnotationDescriptor(MergedAnnotation<Annotation> mergedAnnotation) {
529-
this.annotation = mergedAnnotation.synthesize();
530-
this.root = mergedAnnotation.getRoot();
541+
AnnotationDescriptor(MergedAnnotation<Annotation> annotation) {
542+
this.annotation = annotation.synthesize();
543+
this.root = annotation.getRoot();
531544
}
532545

533546
@Override

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -205,27 +205,25 @@ protected boolean isHandler(Class<?> beanType) {
205205
@Override
206206
protected @Nullable HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
207207
if (this.apiVersionStrategy != null) {
208-
Comparable<?> requestVersion = (Comparable<?>) request.getAttribute(API_VERSION_ATTRIBUTE);
209-
if (requestVersion == null) {
210-
requestVersion = getApiVersion(request, this.apiVersionStrategy);
211-
if (requestVersion != null) {
212-
request.setAttribute(API_VERSION_ATTRIBUTE, requestVersion);
208+
Comparable<?> version = (Comparable<?>) request.getAttribute(API_VERSION_ATTRIBUTE);
209+
if (version == null) {
210+
version = getApiVersion(request, this.apiVersionStrategy);
211+
if (version != null) {
212+
request.setAttribute(API_VERSION_ATTRIBUTE, version);
213213
}
214214
}
215215
}
216216
return super.getHandlerInternal(request);
217217
}
218218

219-
private static @Nullable Comparable<?> getApiVersion(
220-
HttpServletRequest request, ApiVersionStrategy versionStrategy) {
221-
222-
String value = versionStrategy.resolveVersion(request);
219+
private static @Nullable Comparable<?> getApiVersion(HttpServletRequest request, ApiVersionStrategy strategy) {
220+
String value = strategy.resolveVersion(request);
223221
if (value == null) {
224-
return versionStrategy.getDefaultVersion();
222+
return strategy.getDefaultVersion();
225223
}
226224
try {
227-
Comparable<?> version = versionStrategy.parseVersion(value);
228-
versionStrategy.validateVersion(version, request);
225+
Comparable<?> version = strategy.parseVersion(value);
226+
strategy.validateVersion(version, request);
229227
return version;
230228
}
231229
catch (Exception ex) {
@@ -275,42 +273,44 @@ protected boolean isHandler(Class<?> beanType) {
275273
}
276274

277275
private @Nullable RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
278-
RequestMappingInfo requestMappingInfo = null;
276+
277+
List<AnnotationDescriptor> descriptors =
278+
MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none())
279+
.stream()
280+
.filter(MergedAnnotationPredicates.typeIn(RequestMapping.class, HttpExchange.class))
281+
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
282+
.map(AnnotationDescriptor::new)
283+
.distinct()
284+
.toList();
285+
286+
RequestMappingInfo info = null;
279287
RequestCondition<?> customCondition = (element instanceof Class<?> clazz ?
280288
getCustomTypeCondition(clazz) : getCustomMethodCondition((Method) element));
281289

282-
List<AnnotationDescriptor> descriptors = getAnnotationDescriptors(element);
290+
List<AnnotationDescriptor> mappingDescriptors =
291+
descriptors.stream().filter(desc -> desc.annotation instanceof RequestMapping).toList();
283292

284-
List<AnnotationDescriptor> requestMappings = descriptors.stream()
285-
.filter(desc -> desc.annotation instanceof RequestMapping).toList();
286-
if (!requestMappings.isEmpty()) {
287-
if (requestMappings.size() > 1 && logger.isWarnEnabled()) {
288-
logger.warn("Multiple @RequestMapping annotations found on %s, but only the first will be used: %s"
289-
.formatted(element, requestMappings));
290-
}
291-
requestMappingInfo = createRequestMappingInfo((RequestMapping) requestMappings.get(0).annotation, customCondition);
293+
if (!mappingDescriptors.isEmpty()) {
294+
checkMultipleAnnotations(element, mappingDescriptors);
295+
info = createRequestMappingInfo((RequestMapping) mappingDescriptors.get(0).annotation, customCondition);
292296
}
293297

294-
List<AnnotationDescriptor> httpExchanges = descriptors.stream()
295-
.filter(desc -> desc.annotation instanceof HttpExchange).toList();
296-
if (!httpExchanges.isEmpty()) {
297-
Assert.state(requestMappingInfo == null,
298-
() -> "%s is annotated with @RequestMapping and @HttpExchange annotations, but only one is allowed: %s"
299-
.formatted(element, Stream.of(requestMappings, httpExchanges).flatMap(List::stream).toList()));
300-
Assert.state(httpExchanges.size() == 1,
301-
() -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
302-
.formatted(element, httpExchanges));
303-
requestMappingInfo = createRequestMappingInfo((HttpExchange) httpExchanges.get(0).annotation, customCondition);
298+
List<AnnotationDescriptor> exchangeDescriptors =
299+
descriptors.stream().filter(desc -> desc.annotation instanceof HttpExchange).toList();
300+
301+
if (!exchangeDescriptors.isEmpty()) {
302+
checkMultipleAnnotations(element, info, mappingDescriptors, exchangeDescriptors);
303+
info = createRequestMappingInfo((HttpExchange) exchangeDescriptors.get(0).annotation, customCondition);
304304
}
305305

306-
if (requestMappingInfo != null && this.apiVersionStrategy instanceof DefaultApiVersionStrategy davs) {
307-
String version = requestMappingInfo.getVersionCondition().getVersion();
306+
if (info != null && this.apiVersionStrategy instanceof DefaultApiVersionStrategy davs) {
307+
String version = info.getVersionCondition().getVersion();
308308
if (version != null) {
309309
davs.addMappedVersion(version);
310310
}
311311
}
312312

313-
return requestMappingInfo;
313+
return info;
314314
}
315315

316316
/**
@@ -343,6 +343,28 @@ protected boolean isHandler(Class<?> beanType) {
343343
return null;
344344
}
345345

346+
private void checkMultipleAnnotations(
347+
AnnotatedElement element, List<AnnotationDescriptor> mappingDescriptors) {
348+
349+
if (logger.isWarnEnabled() && mappingDescriptors.size() > 1) {
350+
logger.warn("Multiple @RequestMapping annotations found on %s, but only the first will be used: %s"
351+
.formatted(element, mappingDescriptors));
352+
}
353+
}
354+
355+
private static void checkMultipleAnnotations(
356+
AnnotatedElement element, @Nullable RequestMappingInfo info,
357+
List<AnnotationDescriptor> mappingDescriptors, List<AnnotationDescriptor> exchangeDescriptors) {
358+
359+
Assert.state(info == null,
360+
() -> "%s is annotated with @RequestMapping and @HttpExchange annotations, but only one is allowed: %s"
361+
.formatted(element, Stream.of(mappingDescriptors, exchangeDescriptors).flatMap(List::stream).toList()));
362+
363+
Assert.state(exchangeDescriptors.size() == 1,
364+
() -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
365+
.formatted(element, exchangeDescriptors));
366+
}
367+
346368
/**
347369
* Create a {@link RequestMappingInfo} from the supplied
348370
* {@link RequestMapping @RequestMapping} annotation, meta-annotation,
@@ -563,15 +585,6 @@ private String resolveCorsAnnotationValue(String value) {
563585
}
564586
}
565587

566-
private static List<AnnotationDescriptor> getAnnotationDescriptors(AnnotatedElement element) {
567-
return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none())
568-
.stream()
569-
.filter(MergedAnnotationPredicates.typeIn(RequestMapping.class, HttpExchange.class))
570-
.filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex))
571-
.map(AnnotationDescriptor::new)
572-
.distinct()
573-
.toList();
574-
}
575588

576589
private static class AnnotationDescriptor {
577590

0 commit comments

Comments
 (0)