diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java index 054a9762049e..decc8743f8c8 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/AbstractNamedValueMethodArgumentResolver.java @@ -97,9 +97,9 @@ public Object resolveArgumentValue(MethodParameter parameter, Message message arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, message); + handleMissingValue(resolvedName.toString(), nestedParameter, message); } - arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); + arg = handleNullValue(resolvedName.toString(), arg, nestedParameter.getNestedParameterType()); } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); @@ -113,7 +113,7 @@ else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, message); + handleMissingValue(resolvedName.toString(), nestedParameter, message); } } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java index 8f2febc4faf0..cf25ac019ff1 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractNamedValueMethodArgumentResolver.java @@ -105,9 +105,9 @@ public Object resolveArgument(MethodParameter parameter, Message message) thr arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, message); + handleMissingValue(resolvedName.toString(), nestedParameter, message); } - arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); + arg = handleNullValue(resolvedName.toString(), arg, nestedParameter.getNestedParameterType()); } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); @@ -121,7 +121,7 @@ else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, message); + handleMissingValue(resolvedName.toString(), nestedParameter, message); } } } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/reactive/HeaderMethodArgumentResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/reactive/HeaderMethodArgumentResolverTests.java index 940d9a97f652..bf6c8904f6d4 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/reactive/HeaderMethodArgumentResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/reactive/HeaderMethodArgumentResolverTests.java @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.messaging.handler.annotation.MessagingPredicates.header; import static org.springframework.messaging.handler.annotation.MessagingPredicates.headerPlain; @@ -151,6 +152,37 @@ void resolveOptionalHeaderAsEmpty() { assertThat(result).isEqualTo(Optional.empty()); } + @Test + void missingParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + System.setProperty("systemProperty", expected); + Message message = MessageBuilder.withPayload(new byte[0]).build(); + MethodParameter param = this.resolvable.annot(header("#{systemProperties.systemProperty}")).arg(); + + assertThatThrownBy(() -> + resolveArgument(param, message)) + .isInstanceOf(MessageHandlingException.class) + .hasMessageContaining(expected); + + System.clearProperty("systemProperty"); + } + + @Test + void notNullablePrimitiveParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + System.setProperty("systemProperty", expected); + Message message = MessageBuilder.withPayload(new byte[0]).build(); + MethodParameter param = this.resolvable.annot(header("${systemProperty}").required(false)).arg(); + + assertThatThrownBy(() -> + resolver.resolveArgument(param, message)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(expected); + + System.clearProperty("systemProperty"); + } + + @SuppressWarnings({"unchecked", "ConstantConditions"}) private T resolveArgument(MethodParameter param, Message message) { return (T) this.resolver.resolveArgument(param, message).block(Duration.ofSeconds(5)); @@ -165,7 +197,8 @@ public void handleMessage( @Header(name = "#{systemProperties.systemProperty}") String param4, String param5, @Header("foo") Optional param6, - @Header("nativeHeaders.param1") String nativeHeaderParam1) { + @Header("nativeHeaders.param1") String nativeHeaderParam1, + @Header(name = "${systemProperty}", required = false) int primitivePlaceholderParam) { } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java index 713d9600fd83..ed4336b4c66b 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java @@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.messaging.handler.annotation.MessagingPredicates.header; import static org.springframework.messaging.handler.annotation.MessagingPredicates.headerPlain; @@ -137,6 +138,36 @@ void resolveNameFromSystemProperty() throws Exception { } } + @Test + void missingParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + System.setProperty("systemProperty", expected); + Message message = MessageBuilder.withPayload(new byte[0]).build(); + MethodParameter param = this.resolvable.annot(header("#{systemProperties.systemProperty}")).arg(); + + assertThatThrownBy(() -> + resolver.resolveArgument(param, message)) + .isInstanceOf(MessageHandlingException.class) + .hasMessageContaining(expected); + + System.clearProperty("systemProperty"); + } + + @Test + void notNullablePrimitiveParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + System.setProperty("systemProperty", expected); + Message message = MessageBuilder.withPayload(new byte[0]).build(); + MethodParameter param = this.resolvable.annot(header("${systemProperty}").required(false)).arg(); + + assertThatThrownBy(() -> + resolver.resolveArgument(param, message)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(expected); + + System.clearProperty("systemProperty"); + } + @Test void resolveOptionalHeaderWithValue() throws Exception { Message message = MessageBuilder.withPayload("foo").setHeader("foo", "bar").build(); @@ -162,7 +193,8 @@ public void handleMessage( @Header(name = "#{systemProperties.systemProperty}") String param4, String param5, @Header("foo") Optional param6, - @Header("nativeHeaders.param1") String nativeHeaderParam1) { + @Header("nativeHeaders.param1") String nativeHeaderParam1, + @Header(name = "${systemProperty}", required = false) int primitivePlaceholderParam) { } diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java index 332852691ee5..78f4dc67d397 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java @@ -122,10 +122,10 @@ public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAn arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); + handleMissingValue(resolvedName.toString(), nestedParameter, webRequest); } if (!hasDefaultValue) { - arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); + arg = handleNullValue(resolvedName.toString(), arg, nestedParameter.getNestedParameterType()); } } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { @@ -141,7 +141,7 @@ else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = convertIfNecessary(parameter, webRequest, binderFactory, namedValueInfo, arg); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { - handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest); + handleMissingValueAfterConversion(resolvedName.toString(), nestedParameter, webRequest); } } } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java index e9bc98e35065..41f4e9551602 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java @@ -69,6 +69,7 @@ class RequestHeaderMethodArgumentResolverTests { private MethodParameter paramInstant; private MethodParameter paramUuid; private MethodParameter paramUuidOptional; + private MethodParameter paramUuidPlaceholder; private MockHttpServletRequest servletRequest; @@ -93,6 +94,7 @@ void setup() throws Exception { paramInstant = new SynthesizingMethodParameter(method, 8); paramUuid = new SynthesizingMethodParameter(method, 9); paramUuidOptional = new SynthesizingMethodParameter(method, 10); + paramUuidPlaceholder = new SynthesizingMethodParameter(method, 11); servletRequest = new MockHttpServletRequest(); webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse()); @@ -186,6 +188,20 @@ void resolveNameFromSystemPropertyThroughPlaceholder() throws Exception { } } + @Test + void missingParameterFromSystemPropertyThroughPlaceholder() { + String expected = "bar"; + + System.setProperty("systemProperty", expected); + + assertThatThrownBy(() -> + resolver.resolveArgument(paramResolvedNameWithPlaceholder, null, webRequest, null)) + .isInstanceOf(MissingRequestHeaderException.class) + .extracting("headerName").isEqualTo(expected); + + System.clearProperty("systemProperty"); + } + @Test void resolveDefaultValueFromRequest() throws Exception { servletRequest.setContextPath("/bar"); @@ -296,6 +312,24 @@ private void uuidConversionWithEmptyOrBlankValueOptional(String uuid) throws Exc assertThat(result).isNull(); } + @Test + public void uuidPlaceholderConversionWithEmptyValue() { + String expected = "name"; + servletRequest.addHeader(expected, ""); + + System.setProperty("systemProperty", expected); + + ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer(); + bindingInitializer.setConversionService(new DefaultFormattingConversionService()); + + assertThatThrownBy(() -> + resolver.resolveArgument(paramUuidPlaceholder, null, webRequest, + new DefaultDataBinderFactory(bindingInitializer))) + .isInstanceOf(MissingRequestHeaderException.class) + .extracting("headerName").isEqualTo(expected); + + System.clearProperty("systemProperty"); + } void params( @RequestHeader(name = "name", defaultValue = "bar") String param1, @@ -308,7 +342,8 @@ void params( @RequestHeader("name") Date dateParam, @RequestHeader("name") Instant instantParam, @RequestHeader("name") UUID uuid, - @RequestHeader(name = "name", required = false) UUID uuidOptional) { + @RequestHeader(name = "name", required = false) UUID uuidOptional, + @RequestHeader(name = "${systemProperty}") UUID uuidPlaceholder) { } } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java index 82e750965257..c596777fd5a1 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java @@ -23,6 +23,7 @@ import jakarta.servlet.http.Part; import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.propertyeditors.StringTrimmerEditor; @@ -37,7 +38,9 @@ import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebRequestDataBinder; import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.support.MissingServletRequestPartException; @@ -50,6 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.springframework.web.testfixture.method.MvcAnnotationPredicates.requestParam; @@ -64,7 +68,7 @@ */ class RequestParamMethodArgumentResolverTests { - private RequestParamMethodArgumentResolver resolver = new RequestParamMethodArgumentResolver(null, true); + private RequestParamMethodArgumentResolver resolver; private MockHttpServletRequest request = new MockHttpServletRequest(); @@ -72,6 +76,15 @@ class RequestParamMethodArgumentResolverTests { private ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build(); + @BeforeEach + void setup() { + GenericWebApplicationContext context = new GenericWebApplicationContext(); + context.refresh(); + resolver = new RequestParamMethodArgumentResolver(context.getBeanFactory(), true); + + // Expose request to the current thread (for SpEL expressions) + RequestContextHolder.setRequestAttributes(webRequest); + } @Test void supportsParameter() { @@ -141,6 +154,12 @@ void supportsParameter() { param = this.testMethod.annotPresent(RequestPart.class).arg(MultipartFile.class); assertThat(resolver.supportsParameter(param)).isFalse(); + + param = this.testMethod.annotPresent(RequestParam.class).arg(Integer.class); + assertThat(resolver.supportsParameter(param)).isTrue(); + + param = this.testMethod.annotPresent(RequestParam.class).arg(int.class); + assertThat(resolver.supportsParameter(param)).isTrue(); } @Test @@ -678,6 +697,55 @@ void optionalMultipartFileWithoutMultipartRequest() throws Exception { assertThat(actual).isEqualTo(Optional.empty()); } + @Test + void resolveNameFromSystemPropertyThroughPlaceholder() throws Exception { + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(new DefaultConversionService()); + WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); + + Integer expected = 100; + request.addParameter("name", expected.toString()); + + System.setProperty("systemProperty", "name"); + + try { + MethodParameter param = this.testMethod.annot(requestParam().name("${systemProperty}")).arg(Integer.class); + Object result = resolver.resolveArgument(param, null, webRequest, binderFactory); + boolean condition = result instanceof Integer; + assertThat(condition).isTrue(); + } + finally { + System.clearProperty("systemProperty"); + } + } + + @Test + void missingParameterFromSystemPropertyThroughPlaceholder() { + String expected = "name"; + System.setProperty("systemProperty", expected); + + MethodParameter param = this.testMethod.annot(requestParam().name("${systemProperty}")).arg(Integer.class); + assertThatThrownBy(() -> + resolver.resolveArgument(param, null, webRequest, null)) + .isInstanceOf(MissingServletRequestParameterException.class) + .extracting("parameterName").isEqualTo(expected); + + System.clearProperty("systemProperty"); + } + + @Test + void notNullablePrimitiveParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + System.setProperty("systemProperty", expected); + + MethodParameter param = this.testMethod.annot(requestParam().name("${systemProperty}").notRequired()).arg(int.class); + assertThatThrownBy(() -> + resolver.resolveArgument(param, null, webRequest, null)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(expected); + + System.clearProperty("systemProperty"); + } @SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"}) public void handle( @@ -702,7 +770,9 @@ public void handle( @RequestParam("name") Optional paramOptionalArray, @RequestParam("name") Optional> paramOptionalList, @RequestParam("mfile") Optional multipartFileOptional, - @RequestParam(defaultValue = "false") Boolean booleanParam) { + @RequestParam(defaultValue = "false") Boolean booleanParam, + @RequestParam("${systemProperty}") Integer placeholderParam, + @RequestParam(name = "${systemProperty}", required = false) int primitivePlaceholderParam) { } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java index 4aff5cfd2c60..9b0f6018e406 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java @@ -119,7 +119,7 @@ public Mono resolveArgument( return Mono.justOrEmpty(arg); }) .switchIfEmpty(getDefaultValue( - namedValueInfo, parameter, bindingContext, model, exchange)); + namedValueInfo, resolvedName.toString(), parameter, bindingContext, model, exchange)); } /** @@ -218,7 +218,7 @@ private Object applyConversion(@Nullable Object value, NamedValueInfo namedValue /** * Resolve the default value, if any. */ - private Mono getDefaultValue(NamedValueInfo namedValueInfo, MethodParameter parameter, + private Mono getDefaultValue(NamedValueInfo namedValueInfo, String resolvedName, MethodParameter parameter, BindingContext bindingContext, Model model, ServerWebExchange exchange) { return Mono.fromSupplier(() -> { @@ -230,10 +230,10 @@ private Mono getDefaultValue(NamedValueInfo namedValueInfo, MethodParame value = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !parameter.isOptional()) { - handleMissingValue(namedValueInfo.name, parameter, exchange); + handleMissingValue(resolvedName, parameter, exchange); } if (!hasDefaultValue) { - value = handleNullValue(namedValueInfo.name, value, parameter.getNestedParameterType()); + value = handleNullValue(resolvedName, value, parameter.getNestedParameterType()); } if (value != null || !hasDefaultValue) { value = applyConversion(value, namedValueInfo, parameter, bindingContext, exchange); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java index b1a956b64868..596dc36cd261 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java @@ -36,6 +36,7 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.reactive.BindingContext; +import org.springframework.web.server.MissingRequestValueException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest; @@ -43,6 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Tests for {@link RequestHeaderMethodArgumentResolver}. @@ -64,6 +66,7 @@ class RequestHeaderMethodArgumentResolverTests { private MethodParameter paramDate; private MethodParameter paramInstant; private MethodParameter paramMono; + private MethodParameter primitivePlaceholderParam; @BeforeEach @@ -87,6 +90,7 @@ void setup() throws Exception { this.paramDate = new SynthesizingMethodParameter(method, 6); this.paramInstant = new SynthesizingMethodParameter(method, 7); this.paramMono = new SynthesizingMethodParameter(method, 8); + this.primitivePlaceholderParam = new SynthesizingMethodParameter(method, 9); } @@ -200,6 +204,46 @@ void resolveNameFromSystemPropertyThroughPlaceholder() { } } + @Test + void missingParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + MockServerHttpRequest request = MockServerHttpRequest.get("/").build(); + ServerWebExchange exchange = MockServerWebExchange.from(request); + + System.setProperty("systemProperty", expected); + try { + Mono mono = this.resolver.resolveArgument( + this.paramResolvedNameWithExpression, this.bindingContext, exchange); + + assertThatThrownBy(() -> mono.block()) + .isInstanceOf(MissingRequestValueException.class) + .extracting("name").isEqualTo(expected); + } + finally { + System.clearProperty("systemProperty"); + } + } + + @Test + void notNullablePrimitiveParameterFromSystemPropertyThroughPlaceholder() { + String expected = "sysbar"; + MockServerHttpRequest request = MockServerHttpRequest.get("/").build(); + ServerWebExchange exchange = MockServerWebExchange.from(request); + + System.setProperty("systemProperty", expected); + try { + Mono mono = this.resolver.resolveArgument( + this.primitivePlaceholderParam, this.bindingContext, exchange); + + assertThatThrownBy(() -> mono.block()) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining(expected); + } + finally { + System.clearProperty("systemProperty"); + } + } + @Test void notFound() { Mono mono = resolver.resolveArgument( @@ -252,7 +296,8 @@ public void params( @RequestHeader("name") Map unsupported, @RequestHeader("name") Date dateParam, @RequestHeader("name") Instant instantParam, - @RequestHeader Mono alsoNotSupported) { + @RequestHeader Mono alsoNotSupported, + @RequestHeader(value = "${systemProperty}", required = false) int primitivePlaceholderParam) { } }