Skip to content

Commit 7b9cbd7

Browse files
committed
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
1 parent 0758ae5 commit 7b9cbd7

File tree

9 files changed

+190
-56
lines changed

9 files changed

+190
-56
lines changed

framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc

+2-56
Original file line numberDiff line numberDiff line change
@@ -287,32 +287,7 @@ As the preceding example shows, a `ConstraintValidator` implementation can have
287287
You can integrate the method validation feature of Bean Validation into a
288288
Spring context through a `MethodValidationPostProcessor` bean definition:
289289

290-
[tabs]
291-
======
292-
Java::
293-
+
294-
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
295-
----
296-
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
297-
298-
@Configuration
299-
public class AppConfig {
300-
301-
@Bean
302-
public static MethodValidationPostProcessor validationPostProcessor() {
303-
return new MethodValidationPostProcessor();
304-
}
305-
}
306-
307-
----
308-
309-
XML::
310-
+
311-
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
312-
----
313-
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
314-
----
315-
======
290+
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
316291

317292
To be eligible for Spring-driven method validation, target classes need to be annotated
318293
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
345320
you can have `MethodValidationException` raised instead with ``ConstraintViolation``s
346321
adapted to `MessageSourceResolvable` errors. To enable set the following flag:
347322

348-
[tabs]
349-
======
350-
Java::
351-
+
352-
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
353-
----
354-
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
355-
356-
@Configuration
357-
public class AppConfig {
358-
359-
@Bean
360-
public static MethodValidationPostProcessor validationPostProcessor() {
361-
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
362-
processor.setAdaptConstraintViolations(true);
363-
return processor;
364-
}
365-
}
366-
367-
----
368-
369-
XML::
370-
+
371-
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
372-
----
373-
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
374-
<property name="adaptConstraintViolations" value="true"/>
375-
</bean>
376-
----
377-
======
323+
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
378324

379325
`MethodValidationException` contains a list of ``ParameterValidationResult``s which
380326
group errors by method parameter, and each exposes a `MethodParameter`, the argument
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.docs.core.validation.validationbeanvalidationspringmethod;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
22+
23+
// tag::snippet[]
24+
@Configuration
25+
public class ApplicationConfiguration {
26+
27+
@Bean
28+
public static MethodValidationPostProcessor validationPostProcessor() {
29+
return new MethodValidationPostProcessor();
30+
}
31+
}
32+
// end::snippet[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.docs.core.validation.validationbeanvalidationspringmethodexceptions;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
22+
23+
// tag::snippet[]
24+
@Configuration
25+
public class ApplicationConfiguration {
26+
27+
@Bean
28+
public static MethodValidationPostProcessor validationPostProcessor() {
29+
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
30+
processor.setAdaptConstraintViolations(true);
31+
return processor;
32+
}
33+
}
34+
// end::snippet[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.docs.core.validation.validationbeanvalidationspringmethod
18+
19+
import org.springframework.context.annotation.Bean
20+
import org.springframework.context.annotation.Configuration
21+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor
22+
23+
// tag::snippet[]
24+
@Configuration
25+
class ApplicationConfiguration {
26+
27+
companion object {
28+
29+
@Bean
30+
@JvmStatic
31+
fun validationPostProcessor() = MethodValidationPostProcessor()
32+
}
33+
}
34+
// end::snippet[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.docs.core.validation.validationbeanvalidationspringmethodexceptions
18+
19+
import org.springframework.context.annotation.Bean
20+
import org.springframework.context.annotation.Configuration
21+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor
22+
23+
// tag::snippet[]
24+
@Configuration
25+
class ApplicationConfiguration {
26+
27+
companion object {
28+
29+
@Bean
30+
@JvmStatic
31+
fun validationPostProcessor() = MethodValidationPostProcessor().apply {
32+
setAdaptConstraintViolations(true)
33+
}
34+
}
35+
}
36+
// end::snippet[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="
5+
http://www.springframework.org/schema/beans
6+
https://www.springframework.org/schema/beans/spring-beans.xsd">
7+
8+
<!-- tag::snippet[] -->
9+
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
10+
<!-- end::snippet[] -->
11+
</beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="
5+
http://www.springframework.org/schema/beans
6+
https://www.springframework.org/schema/beans/spring-beans.xsd">
7+
8+
<!-- tag::snippet[] -->
9+
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
10+
<property name="adaptConstraintViolations" value="true"/>
11+
</bean>
12+
<!-- end::snippet[] -->
13+
</beans>

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

+3
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ protected final SourceClass doProcessConfigurationClass(
360360
// Process individual @Bean methods
361361
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
362362
for (MethodMetadata methodMetadata : beanMethods) {
363+
if (methodMetadata.isAnnotated("kotlin.jvm.JvmStatic") && !methodMetadata.isStatic()) {
364+
continue;
365+
}
363366
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
364367
}
365368

spring-context/src/test/kotlin/org/springframework/context/annotation/ConfigurationClassKotlinTests.kt

+25
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ package org.springframework.context.annotation
1919
import org.assertj.core.api.Assertions.assertThat
2020
import org.assertj.core.api.Assertions.assertThatExceptionOfType
2121
import org.junit.jupiter.api.Test
22+
import org.springframework.beans.factory.config.BeanPostProcessor
2223
import org.springframework.beans.factory.getBean
2324
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
25+
import org.springframework.beans.factory.support.DefaultListableBeanFactory
2426

2527
/**
2628
* Integration tests for Kotlin configuration classes.
@@ -43,6 +45,16 @@ class ConfigurationClassKotlinTests {
4345
assertThat(context.getBean<Bar>().foo).isEqualTo(foo)
4446
}
4547

48+
@Test
49+
fun `Configuration with @JvmStatic registers a single bean`() {
50+
val beanFactory = DefaultListableBeanFactory().apply {
51+
isAllowBeanDefinitionOverriding = false
52+
}
53+
val context = AnnotationConfigApplicationContext(beanFactory)
54+
context.register(ProcessorConfiguration::class.java)
55+
context.refresh()
56+
}
57+
4658

4759
@Configuration
4860
class FinalConfigurationWithProxy {
@@ -64,6 +76,19 @@ class ConfigurationClassKotlinTests {
6476
fun bar(foo: Foo) = Bar(foo)
6577
}
6678

79+
@Configuration
80+
open class ProcessorConfiguration {
81+
82+
companion object {
83+
84+
@Bean
85+
@JvmStatic
86+
fun processor(): BeanPostProcessor {
87+
return object: BeanPostProcessor{}
88+
}
89+
}
90+
}
91+
6792
class Foo
6893

6994
class Bar(val foo: Foo)

0 commit comments

Comments
 (0)