Skip to content

Commit

Permalink
Add support for Kotlin BeanPostProcessor beans
Browse files Browse the repository at this point in the history
This commit adds support for Kotlin BeanPostProcessor beans which should
be defined in a companion object and annotated with `@JvmStatic`.

Closes gh-32946
  • Loading branch information
sdeleuze committed Jun 6, 2024
1 parent 0758ae5 commit 7b9cbd7
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
----
======
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
Expand Down Expand Up @@ -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"]
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
----
======
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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[]
Original file line number Diff line number Diff line change
@@ -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[]
Original file line number Diff line number Diff line change
@@ -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[]
Original file line number Diff line number Diff line change
@@ -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[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- tag::snippet[] -->
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
<!-- end::snippet[] -->
</beans>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- tag::snippet[] -->
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
<!-- end::snippet[] -->
</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ protected final SourceClass doProcessConfigurationClass(
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
if (methodMetadata.isAnnotated("kotlin.jvm.JvmStatic") && !methodMetadata.isStatic()) {
continue;
}
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -43,6 +45,16 @@ class ConfigurationClassKotlinTests {
assertThat(context.getBean<Bar>().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 {
Expand All @@ -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)
Expand Down

0 comments on commit 7b9cbd7

Please sign in to comment.