diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc index 5616e8110b..ab6c1f647e 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc @@ -19,7 +19,7 @@ Java:: private String name; - private MultipartFile file; + private FilePart file; // ... @@ -42,7 +42,7 @@ Kotlin:: ---- class MyForm( val name: String, - val file: MultipartFile) + val file: FilePart) @Controller class FileUploadController { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index 976eff8f5d..1b07d10165 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -839,7 +839,7 @@ public interface WebClient { Flux bodyToFlux(Class elementClass); /** - * Variant of {@link #bodyToMono(Class)} with a {@link ParameterizedTypeReference}. + * Variant of {@link #bodyToFlux(Class)} with a {@link ParameterizedTypeReference}. * @param elementTypeRef the type of element to decode to * @param the body element type * @return the decoded body @@ -859,7 +859,7 @@ public interface WebClient { Mono> toEntity(Class bodyClass); /** - * Variant of {@link #bodyToMono(Class)} with a {@link ParameterizedTypeReference}. + * Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}. * @param bodyTypeReference the expected response body type * @param the response body type * @return the {@code ResponseEntity} with the decoded body @@ -881,7 +881,7 @@ public interface WebClient { Mono>> toEntityList(Class elementClass); /** - * Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}. + * Variant of {@link #toEntityList(Class)} with a {@link ParameterizedTypeReference}. * @param elementTypeRef the type of element to decode the target Flux to * @param the body element type * @return the {@code ResponseEntity} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java index c3f8426e45..25eeec851a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,6 +104,8 @@ public class HandlerMappingIntrospector private static final String CACHED_RESULT_ATTRIBUTE = HandlerMappingIntrospector.class.getName() + ".CachedResult"; + private static final int DEFAULT_CACHE_LIMIT = 2048; + private @Nullable ApplicationContext applicationContext; @@ -111,9 +113,30 @@ public class HandlerMappingIntrospector private Map pathPatternMappings = Collections.emptyMap(); + private int patternCacheLimit = DEFAULT_CACHE_LIMIT; + private final CacheResultLogHelper cacheLogHelper = new CacheResultLogHelper(); + /** + * Set a limit on the maximum number of security patterns passed into + * {@link MatchableHandlerMapping#match} at runtime to cache. + *

By default, this is set to 2048. + * @param patternCacheLimit the limit to use + * @since 6.2.8 + */ + public void setPatternCacheLimit(int patternCacheLimit) { + this.patternCacheLimit = patternCacheLimit; + } + + /** + * Return the configured limit on security patterns to cache. + * @since 6.2.8 + */ + public int getPatternCacheLimit() { + return this.patternCacheLimit; + } + @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; @@ -127,8 +150,9 @@ public class HandlerMappingIntrospector this.pathPatternMappings = this.handlerMappings.stream() .filter(m -> m instanceof MatchableHandlerMapping hm && hm.getPatternParser() != null) - .map(mapping -> (MatchableHandlerMapping) mapping) - .collect(Collectors.toMap(mapping -> mapping, PathPatternMatchableHandlerMapping::new)); + .map(hm -> (MatchableHandlerMapping) hm) + .collect(Collectors.toMap(hm -> hm, (MatchableHandlerMapping delegate) -> + new PathPatternMatchableHandlerMapping(delegate, getPatternCacheLimit()))); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/PathPatternMatchableHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/PathPatternMatchableHandlerMapping.java index 12aff02698..725869f2d3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/PathPatternMatchableHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/PathPatternMatchableHandlerMapping.java @@ -42,21 +42,21 @@ import org.springframework.web.util.pattern.PathPatternParser; @Deprecated(since = "7.0", forRemoval = true) class PathPatternMatchableHandlerMapping implements MatchableHandlerMapping { - private static final int MAX_PATTERNS = 1024; - - private final MatchableHandlerMapping delegate; private final PathPatternParser parser; private final Map pathPatternCache = new ConcurrentHashMap<>(); + private final int cacheLimit; - public PathPatternMatchableHandlerMapping(MatchableHandlerMapping delegate) { + + public PathPatternMatchableHandlerMapping(MatchableHandlerMapping delegate, int cacheLimit) { Assert.notNull(delegate, "HandlerMapping to delegate to is required."); Assert.notNull(delegate.getPatternParser(), "Expected HandlerMapping configured to use PatternParser."); this.delegate = delegate; this.parser = delegate.getPatternParser(); + this.cacheLimit = cacheLimit; } @SuppressWarnings("removal") @@ -64,7 +64,7 @@ class PathPatternMatchableHandlerMapping implements MatchableHandlerMapping { @Override public @Nullable RequestMatchResult match(HttpServletRequest request, String pattern) { PathPattern pathPattern = this.pathPatternCache.computeIfAbsent(pattern, value -> { - Assert.state(this.pathPatternCache.size() < MAX_PATTERNS, "Max size for pattern cache exceeded."); + Assert.state(this.pathPatternCache.size() < this.cacheLimit, "Max size for pattern cache exceeded."); return this.parser.parse(pattern); }); PathContainer path = ServletRequestPathUtils.getParsedRequestPath(request).pathWithinApplication();