From 7b9cbd78761428d2607fd12e63ee0b3e5112ba43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Thu, 6 Jun 2024 19:23:25 +0200 Subject: [PATCH] Add support for Kotlin BeanPostProcessor beans This commit adds support for Kotlin BeanPostProcessor beans which should be defined in a companion object and annotated with `@JvmStatic`. Closes gh-32946 --- .../pages/core/validation/beanvalidation.adoc | 58 +------------------ .../ApplicationConfiguration.java | 32 ++++++++++ .../ApplicationConfiguration.java | 34 +++++++++++ .../ApplicationConfiguration.kt | 34 +++++++++++ .../ApplicationConfiguration.kt | 36 ++++++++++++ .../ApplicationConfiguration.xml | 11 ++++ .../ApplicationConfiguration.xml | 13 +++++ .../annotation/ConfigurationClassParser.java | 3 + .../ConfigurationClassKotlinTests.kt | 25 ++++++++ 9 files changed, 190 insertions(+), 56 deletions(-) create mode 100644 framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.java create mode 100644 framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.java create mode 100644 framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.kt create mode 100644 framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.kt create mode 100644 framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.xml create mode 100644 framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.xml diff --git a/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc b/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc index f97c38c85d4c..716a0309c222 100644 --- a/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc +++ b/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc @@ -287,32 +287,7 @@ As the preceding example shows, a `ConstraintValidator` implementation can have You can integrate the method validation feature of Bean Validation into a Spring context through a `MethodValidationPostProcessor` bean definition: -[tabs] -====== -Java:: -+ -[source,java,indent=0,subs="verbatim,quotes",role="primary"] ----- - import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; - - @Configuration - public class AppConfig { - - @Bean - public static MethodValidationPostProcessor validationPostProcessor() { - return new MethodValidationPostProcessor(); - } - } - ----- - -XML:: -+ -[source,xml,indent=0,subs="verbatim,quotes",role="secondary"] ----- - ----- -====== +include-code::./ApplicationConfiguration[tag=snippet,indent=0] To be eligible for Spring-driven method validation, target classes need to be annotated with Spring's `@Validated` annotation, which can optionally also declare the validation @@ -345,36 +320,7 @@ By default, `jakarta.validation.ConstraintViolationException` is raised with the you can have `MethodValidationException` raised instead with ``ConstraintViolation``s adapted to `MessageSourceResolvable` errors. To enable set the following flag: -[tabs] -====== -Java:: -+ -[source,java,indent=0,subs="verbatim,quotes",role="primary"] ----- - import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; - - @Configuration - public class AppConfig { - - @Bean - public static MethodValidationPostProcessor validationPostProcessor() { - MethodValidationPostProcessor processor = new MethodValidationPostProcessor(); - processor.setAdaptConstraintViolations(true); - return processor; - } - } - ----- - -XML:: -+ -[source,xml,indent=0,subs="verbatim,quotes",role="secondary"] ----- - - - ----- -====== +include-code::./ApplicationConfiguration[tag=snippet,indent=0] `MethodValidationException` contains a list of ``ParameterValidationResult``s which group errors by method parameter, and each exposes a `MethodParameter`, the argument diff --git a/framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.java b/framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.java new file mode 100644 index 000000000000..c25e2ab073d3 --- /dev/null +++ b/framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2024 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.docs.core.validation.validationbeanvalidationspringmethod; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; + +// tag::snippet[] +@Configuration +public class ApplicationConfiguration { + + @Bean + public static MethodValidationPostProcessor validationPostProcessor() { + return new MethodValidationPostProcessor(); + } +} +// end::snippet[] diff --git a/framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.java b/framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.java new file mode 100644 index 000000000000..ad18753eafe3 --- /dev/null +++ b/framework-docs/src/main/java/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2024 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.docs.core.validation.validationbeanvalidationspringmethodexceptions; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; + +// tag::snippet[] +@Configuration +public class ApplicationConfiguration { + + @Bean + public static MethodValidationPostProcessor validationPostProcessor() { + MethodValidationPostProcessor processor = new MethodValidationPostProcessor(); + processor.setAdaptConstraintViolations(true); + return processor; + } +} +// end::snippet[] diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.kt b/framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.kt new file mode 100644 index 000000000000..be0946c8376a --- /dev/null +++ b/framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2024 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.docs.core.validation.validationbeanvalidationspringmethod + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor + +// tag::snippet[] +@Configuration +class ApplicationConfiguration { + + companion object { + + @Bean + @JvmStatic + fun validationPostProcessor() = MethodValidationPostProcessor() + } +} +// end::snippet[] diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.kt b/framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.kt new file mode 100644 index 000000000000..07e383481edb --- /dev/null +++ b/framework-docs/src/main/kotlin/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2024 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.docs.core.validation.validationbeanvalidationspringmethodexceptions + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor + +// tag::snippet[] +@Configuration +class ApplicationConfiguration { + + companion object { + + @Bean + @JvmStatic + fun validationPostProcessor() = MethodValidationPostProcessor().apply { + setAdaptConstraintViolations(true) + } + } +} +// end::snippet[] \ No newline at end of file diff --git a/framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.xml b/framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.xml new file mode 100644 index 000000000000..2d0fc9029411 --- /dev/null +++ b/framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethod/ApplicationConfiguration.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.xml b/framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.xml new file mode 100644 index 000000000000..c7477ce108d4 --- /dev/null +++ b/framework-docs/src/main/resources/org/springframework/docs/core/validation/validationbeanvalidationspringmethodexceptions/ApplicationConfiguration.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index b4356d8957b6..cddae13bae92 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -360,6 +360,9 @@ protected final SourceClass doProcessConfigurationClass( // Process individual @Bean methods Set beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { + if (methodMetadata.isAnnotated("kotlin.jvm.JvmStatic") && !methodMetadata.isStatic()) { + continue; + } configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } diff --git a/spring-context/src/test/kotlin/org/springframework/context/annotation/ConfigurationClassKotlinTests.kt b/spring-context/src/test/kotlin/org/springframework/context/annotation/ConfigurationClassKotlinTests.kt index a12e9d747913..43158dbd0791 100644 --- a/spring-context/src/test/kotlin/org/springframework/context/annotation/ConfigurationClassKotlinTests.kt +++ b/spring-context/src/test/kotlin/org/springframework/context/annotation/ConfigurationClassKotlinTests.kt @@ -19,8 +19,10 @@ package org.springframework.context.annotation import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.jupiter.api.Test +import org.springframework.beans.factory.config.BeanPostProcessor import org.springframework.beans.factory.getBean import org.springframework.beans.factory.parsing.BeanDefinitionParsingException +import org.springframework.beans.factory.support.DefaultListableBeanFactory /** * Integration tests for Kotlin configuration classes. @@ -43,6 +45,16 @@ class ConfigurationClassKotlinTests { assertThat(context.getBean().foo).isEqualTo(foo) } + @Test + fun `Configuration with @JvmStatic registers a single bean`() { + val beanFactory = DefaultListableBeanFactory().apply { + isAllowBeanDefinitionOverriding = false + } + val context = AnnotationConfigApplicationContext(beanFactory) + context.register(ProcessorConfiguration::class.java) + context.refresh() + } + @Configuration class FinalConfigurationWithProxy { @@ -64,6 +76,19 @@ class ConfigurationClassKotlinTests { fun bar(foo: Foo) = Bar(foo) } + @Configuration + open class ProcessorConfiguration { + + companion object { + + @Bean + @JvmStatic + fun processor(): BeanPostProcessor { + return object: BeanPostProcessor{} + } + } + } + class Foo class Bar(val foo: Foo)