diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 5a42d993f32..944587faacd 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -6840,7 +6840,7 @@ The following example shows a `RestDocumentationResultHandler` being defined: [source,java,indent=0] ---- - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class ResultHandlerConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/restassured/AdvancedConfigurationExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/restassured/AdvancedConfigurationExample.java index cc89f3a3d5e..1e3247b893f 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/restassured/AdvancedConfigurationExample.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/restassured/AdvancedConfigurationExample.java @@ -24,7 +24,7 @@ import org.springframework.restdocs.templates.TemplateFormats; public class AdvancedConfigurationExample { // tag::configuration[] - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) public static class CustomizationConfiguration implements RestDocsRestAssuredConfigurationCustomizer { @Override diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/webclient/AdvancedConfigurationExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/webclient/AdvancedConfigurationExample.java index 3491726cb27..a5b574a59dc 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/webclient/AdvancedConfigurationExample.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/test/autoconfigure/restdocs/webclient/AdvancedConfigurationExample.java @@ -23,7 +23,7 @@ import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation public class AdvancedConfigurationExample { // tag::configuration[] - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) public static class CustomizationConfiguration implements RestDocsWebTestClientConfigurationCustomizer { @Override diff --git a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/web/client/SampleWebClientTests.java b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/web/client/SampleWebClientTests.java index f6c39e38851..117b892a3bd 100644 --- a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/web/client/SampleWebClientTests.java +++ b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/web/client/SampleWebClientTests.java @@ -49,7 +49,7 @@ class SampleWebClientTests { assertThat(headers.getLocation()).hasHost("other.example.com"); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class Config { @Bean diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/ExampleTestConfig.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/ExampleTestConfig.java index afac195f504..59e086bf7e4 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/ExampleTestConfig.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/ExampleTestConfig.java @@ -25,7 +25,7 @@ import org.springframework.boot.test.context.TestConfiguration; * * @author Phillip Webb */ -@TestConfiguration +@TestConfiguration(proxyBeanMethods = false) @EntityScan("some.other.package") public class ExampleTestConfig { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseWithNoDatabaseIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseWithNoDatabaseIntegrationTests.java index 00815827375..6e2e2d932de 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseWithNoDatabaseIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseWithNoDatabaseIntegrationTests.java @@ -48,7 +48,7 @@ class AutoConfigureTestDatabaseWithNoDatabaseIntegrationTests { assertThat(this.context.getBeanNamesForType(DataSource.class)).isNotEmpty(); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class Config { } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/MockMvcRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/MockMvcRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java index d36b7cf38f4..40d41b3c611 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/MockMvcRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/MockMvcRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java @@ -78,7 +78,7 @@ class MockMvcRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests { assertThat(new File(defaultSnippetsDir, "response-fields.md")).isFile(); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class CustomizationConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java index 3fb4d3b5be1..0ef4cd8cfc6 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java @@ -82,7 +82,7 @@ class RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests assertThat(new File(defaultSnippetsDir, "response-fields.md")).isFile(); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class CustomizationConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java index 8dac2c1ef2e..eb870231310 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java @@ -72,7 +72,7 @@ class WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTest assertThat(new File(defaultSnippetsDir, "response-fields.md")).isFile(); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class CustomizationConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestServletFilterRegistrationDisabledIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestServletFilterRegistrationDisabledIntegrationTests.java index b6727b92597..d0ab9210be3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestServletFilterRegistrationDisabledIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestServletFilterRegistrationDisabledIntegrationTests.java @@ -44,7 +44,7 @@ class WebMvcTestServletFilterRegistrationDisabledIntegrationTests { this.mvc.perform(get("/one")).andExpect(header().string("x-test", (String) null)); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class DisabledRegistrationConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/TestConfiguration.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/TestConfiguration.java index afadef9401c..4fee6426fbf 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/TestConfiguration.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/TestConfiguration.java @@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.SpringBootConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AliasFor; @@ -39,7 +40,7 @@ import org.springframework.core.annotation.AliasFor; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Configuration(proxyBeanMethods = false) +@Configuration @TestComponent public @interface TestConfiguration { @@ -51,4 +52,29 @@ public @interface TestConfiguration { @AliasFor(annotation = Configuration.class) String value() default ""; + /** + * Specify whether {@link Bean @Bean} methods should get proxied in order to enforce + * bean lifecycle behavior, e.g. to return shared singleton bean instances even in + * case of direct {@code @Bean} method calls in user code. This feature requires + * method interception, implemented through a runtime-generated CGLIB subclass which + * comes with limitations such as the configuration class and its methods not being + * allowed to declare {@code final}. + *
+ * The default is {@code true}, allowing for 'inter-bean references' within the + * configuration class as well as for external calls to this configuration's + * {@code @Bean} methods, e.g. from another configuration class. If this is not needed + * since each of this particular configuration's {@code @Bean} methods is + * self-contained and designed as a plain factory method for container use, switch + * this flag to {@code false} in order to avoid CGLIB subclass processing. + *
+ * Turning off bean method interception effectively processes {@code @Bean} methods + * individually like when declared on non-{@code @Configuration} classes, a.k.a. + * "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore behaviorally + * equivalent to removing the {@code @Configuration} stereotype. + * @return whether to proxy {@code @Bean} methods + * @since 2.2.1 + */ + @AliasFor(annotation = Configuration.class) + boolean proxyBeanMethods() default true; + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/TestConfigurationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/TestConfigurationTests.java new file mode 100644 index 00000000000..1984d4e648e --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/TestConfigurationTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2019 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.boot.test.context; + +import org.junit.jupiter.api.Test; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link TestConfiguration @TestConfiguration}. + * + * @author Stephane Nicoll + */ +class TestConfigurationTests { + + @Test + void proxyBeanMethodsIsEnabledByDefault() { + AnnotationAttributes attributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(DefaultTestConfiguration.class, Configuration.class); + assertThat(attributes.get("proxyBeanMethods")).isEqualTo(true); + } + + @Test + void proxyBeanMethodsCanBeDisabled() { + AnnotationAttributes attributes = AnnotatedElementUtils + .getMergedAnnotationAttributes(NoBeanMethodProxyingTestConfiguration.class, Configuration.class); + assertThat(attributes.get("proxyBeanMethods")).isEqualTo(false); + } + + @TestConfiguration + static class DefaultTestConfiguration { + + } + + @TestConfiguration(proxyBeanMethods = false) + static class NoBeanMethodProxyingTestConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java index 66bc35a0642..9f663e68776 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java @@ -67,7 +67,7 @@ class SpringBootTestContextBootstrapperIntegrationTests { assertThat(this.defaultTestExecutionListenersPostProcessorCalled).isTrue(); } - @TestConfiguration + @TestConfiguration(proxyBeanMethods = false) static class TestConfig { @Bean