diff --git a/spring-context/spring-context.gradle b/spring-context/spring-context.gradle index 5f5ac1001e..04d4e1e8c7 100644 --- a/spring-context/spring-context.gradle +++ b/spring-context/spring-context.gradle @@ -36,6 +36,7 @@ dependencies { testImplementation("org.apache.commons:commons-pool2") testImplementation("org.awaitility:awaitility") testImplementation("jakarta.inject:jakarta.inject-tck") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") testRuntimeOnly("jakarta.xml.bind:jakarta.xml.bind-api") testRuntimeOnly("org.glassfish:jakarta.el") // Substitute for javax.management:jmxremote_optional:1.0.1_04 (not available on Maven Central) diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java index 827ee3fd89..6a0f939c4f 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -49,8 +49,6 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.MessageSource; -import org.springframework.core.KotlinDetector; -import org.springframework.core.KotlinReflectionParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.io.Resource; import org.springframework.lang.Nullable; @@ -75,6 +73,7 @@ import org.springframework.util.ReflectionUtils; * {@code jakarta.validation} API being present but no explicit Validator having been configured. * * @author Juergen Hoeller + * @author Sebastien Deleuze * @since 3.0 * @see jakarta.validation.ValidatorFactory * @see jakarta.validation.Validator @@ -118,13 +117,6 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter private ValidatorFactory validatorFactory; - public LocalValidatorFactoryBean() { - if (KotlinDetector.isKotlinReflectPresent()) { - this.parameterNameDiscoverer = new KotlinReflectionParameterNameDiscoverer(); - } - } - - /** * Specify the desired provider class, if any. *

If not specified, JSR-303's default search mechanism will be used. @@ -196,9 +188,8 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter /** * Set the ParameterNameDiscoverer to use for resolving method and constructor * parameter names if needed for message interpolation. - *

Default is Hibernate Validator's own internal use of standard Java reflection, - * with an additional {@link KotlinReflectionParameterNameDiscoverer} if Kotlin - * is present. This may be overridden with a custom subclass or a Spring-controlled + *

Default is Hibernate Validator's own internal use of standard Java reflection. + * This may be overridden with a custom subclass or a Spring-controlled * {@link org.springframework.core.DefaultParameterNameDiscoverer} if necessary. */ public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { diff --git a/spring-context/src/test/kotlin/org/springframework/validation/beanvalidation/KotlinMethodValidationTests.kt b/spring-context/src/test/kotlin/org/springframework/validation/beanvalidation/KotlinMethodValidationTests.kt new file mode 100644 index 0000000000..ba3b6f93f5 --- /dev/null +++ b/spring-context/src/test/kotlin/org/springframework/validation/beanvalidation/KotlinMethodValidationTests.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2023 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.validation.beanvalidation + +import jakarta.validation.ValidationException +import jakarta.validation.Validator +import jakarta.validation.constraints.NotEmpty +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import org.springframework.aop.framework.ProxyFactory +import org.springframework.validation.annotation.Validated + +/** + * Kotlin tests for [MethodValidationInterceptor] + [LocalValidatorFactoryBean]. + * + * @author Sebastien Deleuze + */ +@Suppress("UsePropertyAccessSyntax") +class KotlinMethodValidationTests { + + @Test + fun parameterValidation() { + val bean = MyValidBean() + val proxyFactory = ProxyFactory(bean) + val validator = LocalValidatorFactoryBean() + validator.afterPropertiesSet() + proxyFactory.addAdvice(MethodValidationInterceptor(validator as Validator)) + val proxy = proxyFactory.getProxy() as MyValidBean + Assertions.assertThat(proxy.validName("name")).isEqualTo("name") + Assertions.assertThatExceptionOfType(ValidationException::class.java).isThrownBy { + proxy.validName("") + } + } + + @Test + fun coroutinesParameterValidation() = runBlocking { + val bean = MyValidCoroutinesBean() + val proxyFactory = ProxyFactory(bean) + val validator = LocalValidatorFactoryBean() + validator.afterPropertiesSet() + proxyFactory.addAdvice(MethodValidationInterceptor(validator as Validator)) + val proxy = proxyFactory.getProxy() as MyValidCoroutinesBean + Assertions.assertThat(proxy.validName("name")).isEqualTo("name") + Assertions.assertThatExceptionOfType(ValidationException::class.java).isThrownBy { + runBlocking { + proxy.validName("") + } + } + } + + @Validated + open class MyValidBean { + + @Suppress("UNUSED_PARAMETER") + open fun validName(@NotEmpty name: String) = name + } + + @Validated + open class MyValidCoroutinesBean { + + @Suppress("UNUSED_PARAMETER") + open suspend fun validName(@NotEmpty name: String) = name + } + +}