Introduce DynamicPropertyRegistrar to replace DynamicPropertyRegistry bean
Spring Boot's testing support registers a DynamicPropertyRegistry as a bean in the ApplicationContext, which conflicts with the DynamicPropertyRegistry registered as a bean by the Spring TestContext Framework (TCF) since Spring Framework 6.2 M2. To avoid that conflict and to improve the user experience for Spring's testing support, this commit introduces a DynamicPropertyRegistrar API to replace the DynamicPropertyRegistry bean support. Specifically, the TCF no longer registers a DynamicPropertyRegistry as a bean in the ApplicationContext. Instead, users can now register custom implementations of DynamicPropertyRegistrar as beans in the ApplicationContext, and the DynamicPropertiesContextCustomizer now registers a DynamicPropertyRegistrarBeanInitializer which eagerly initializes DynamicPropertyRegistrar beans and invokes their accept() methods with an appropriate DynamicPropertyRegistry. In addition, a singleton DynamicValuesPropertySource is created and registered with the Environment for use in DynamicPropertiesContextCustomizer and DynamicPropertyRegistrarBeanInitializer, which allows @DynamicPropertySource methods and DynamicPropertyRegistrar beans to transparently populate the same DynamicValuesPropertySource. Closes gh-33501
This commit is contained in:
parent
78028cde05
commit
e7b52cf8b2
|
|
@ -2,39 +2,51 @@
|
|||
= Context Configuration with Dynamic Property Sources
|
||||
|
||||
The Spring TestContext Framework provides support for _dynamic_ properties via the
|
||||
`@DynamicPropertySource` annotation and the `DynamicPropertyRegistry`.
|
||||
`DynamicPropertyRegistry`, the `@DynamicPropertySource` annotation, and the
|
||||
`DynamicPropertyRegistrar` API.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The `@DynamicPropertySource` annotation and its supporting infrastructure were originally
|
||||
designed to allow properties from {testcontainers-site}[Testcontainers] based tests to be
|
||||
exposed easily to Spring integration tests. However, this feature may be used with any
|
||||
form of external resource whose lifecycle is managed outside the test's
|
||||
`ApplicationContext` or with beans whose lifecycle is managed by the test's
|
||||
`ApplicationContext`.
|
||||
The dynamic property source infrastructure was originally designed to allow properties
|
||||
from {testcontainers-site}[Testcontainers] based tests to be exposed easily to Spring
|
||||
integration tests. However, these features may be used with any form of external resource
|
||||
whose lifecycle is managed outside the test's `ApplicationContext` or with beans whose
|
||||
lifecycle is managed by the test's `ApplicationContext`.
|
||||
====
|
||||
|
||||
|
||||
[[testcontext-ctx-management-dynamic-property-sources-precedence]]
|
||||
== Precedence
|
||||
|
||||
Dynamic properties have higher precedence than those loaded from `@TestPropertySource`,
|
||||
the operating system's environment, Java system properties, or property sources added by
|
||||
the application declaratively by using `@PropertySource` or programmatically. Thus,
|
||||
dynamic properties can be used to selectively override properties loaded via
|
||||
`@TestPropertySource`, system property sources, and application property sources.
|
||||
|
||||
|
||||
[[testcontext-ctx-management-dynamic-property-sources-dynamic-property-registry]]
|
||||
== `DynamicPropertyRegistry`
|
||||
|
||||
A `DynamicPropertyRegistry` is used to add _name-value_ pairs to the `Environment`.
|
||||
Values are dynamic and provided via a `Supplier` which is only invoked when the property
|
||||
is resolved. Typically, method references are used to supply values. The following
|
||||
sections provide examples of how to use the `DynamicPropertyRegistry`.
|
||||
|
||||
|
||||
[[testcontext-ctx-management-dynamic-property-sources-dynamic-property-source]]
|
||||
== `@DynamicPropertySource`
|
||||
|
||||
In contrast to the
|
||||
xref:testing/testcontext-framework/ctx-management/property-sources.adoc[`@TestPropertySource`]
|
||||
annotation that is applied at the class level, `@DynamicPropertySource` can be applied to
|
||||
`static` methods in integration test classes or to `@Bean` methods in test
|
||||
`@Configuration` classes in order to add properties with dynamic values to the set of
|
||||
`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the
|
||||
integration test.
|
||||
|
||||
A `DynamicPropertyRegistry` is used to add _name-value_ pairs to the `Environment`.
|
||||
Values are dynamic and provided via a `Supplier` which is only invoked when the property
|
||||
is resolved. Typically, method references are used to supply values.
|
||||
`static` methods in integration test classes in order to add properties with dynamic
|
||||
values to the set of `PropertySources` in the `Environment` for the `ApplicationContext`
|
||||
loaded for the integration test.
|
||||
|
||||
Methods in integration test classes that are annotated with `@DynamicPropertySource` must
|
||||
be `static` and must accept a single `DynamicPropertyRegistry` argument.
|
||||
|
||||
`@Bean` methods annotated with `@DynamicPropertySource` may either accept an argument of
|
||||
type `DynamicPropertyRegistry` or access a `DynamicPropertyRegistry` instance autowired
|
||||
into their enclosing `@Configuration` class. Note, however, that `@Bean` methods which
|
||||
interact with a `DynamicPropertyRegistry` are not required to be annotated with
|
||||
`@DynamicPropertySource` unless they need to enforce eager initialization of the bean
|
||||
within the context. See the class-level javadoc for `DynamicPropertyRegistry` for details.
|
||||
be `static` and must accept a single `DynamicPropertyRegistry` argument. See the
|
||||
class-level javadoc for `DynamicPropertyRegistry` for further details.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
|
|
@ -107,11 +119,33 @@ Kotlin::
|
|||
----
|
||||
======
|
||||
|
||||
The following example demonstrates how to use `DynamicPropertyRegistry` and
|
||||
`@DynamicPropertySource` with a `@Bean` method. The `api.url` property can be accessed
|
||||
via Spring's `Environment` abstraction or injected directly into other Spring-managed
|
||||
components – for example, via `@Value("${api.url}")`. The value of the `api.url` property
|
||||
will be dynamically retrieved from the `ApiServer` bean.
|
||||
|
||||
[[testcontext-ctx-management-dynamic-property-sources-dynamic-property-registrar]]
|
||||
== `DynamicPropertyRegistrar`
|
||||
|
||||
As an alternative to implementing `@DynamicPropertySource` methods in integration test
|
||||
classes, you can register implementations of the `DynamicPropertyRegistrar` API as beans
|
||||
within the test's `ApplicationContext`. Doing so allows you to support additional use
|
||||
cases that are not possible with a `@DynamicPropertySource` method. For example, since a
|
||||
`DynamicPropertyRegistrar` is itself a bean in the `ApplicationContext`, it can interact
|
||||
with other beans in the context and register dynamic properties that are sourced from
|
||||
those beans.
|
||||
|
||||
Any bean in a test's `ApplicationContext` that implements the `DynamicPropertyRegistrar`
|
||||
interface will be automatically detected and eagerly initialized before the singleton
|
||||
pre-instantiation phase, and the `accept()` methods of such beans will be invoked with a
|
||||
`DynamicPropertyRegistry` that performs the actual dynamic property registration on
|
||||
behalf of the registrar.
|
||||
|
||||
WARNING: Any interaction with other beans results in eager initialization of those other
|
||||
beans and their dependencies.
|
||||
|
||||
The following example demonstrates how to implement a `DynamicPropertyRegistrar` as a
|
||||
lambda expression that registers a dynamic property for the `ApiServer` bean. The
|
||||
`api.url` property can be accessed via Spring's `Environment` abstraction or injected
|
||||
directly into other Spring-managed components – for example, via `@Value("${api.url}")`,
|
||||
and the value of the `api.url` property will be dynamically retrieved from the
|
||||
`ApiServer` bean.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
|
@ -123,11 +157,13 @@ Java::
|
|||
class TestConfig {
|
||||
|
||||
@Bean
|
||||
@DynamicPropertySource
|
||||
ApiServer apiServer(DynamicPropertyRegistry registry) {
|
||||
ApiServer apiServer = new ApiServer();
|
||||
registry.add("api.url", apiServer::getUrl);
|
||||
return apiServer;
|
||||
ApiServer apiServer() {
|
||||
return new ApiServer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
DynamicPropertyRegistrar apiServerProperties(ApiServer apiServer) {
|
||||
return registry -> registry.add("api.url", apiServer::getUrl);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
|
@ -140,27 +176,14 @@ Kotlin::
|
|||
class TestConfig {
|
||||
|
||||
@Bean
|
||||
@DynamicPropertySource
|
||||
fun apiServer(registry: DynamicPropertyRegistry): ApiServer {
|
||||
val apiServer = ApiServer()
|
||||
registry.add("api.url", apiServer::getUrl)
|
||||
return apiServer
|
||||
fun apiServer(): ApiServer {
|
||||
return ApiServer()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun apiServerProperties(apiServer: ApiServer): DynamicPropertyRegistrar {
|
||||
return registry -> registry.add("api.url", apiServer::getUrl)
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: The use of `@DynamicPropertySource` on the `@Bean` method is optional and results
|
||||
in the `ApiServer` bean being eagerly initialized so that other beans in the context can
|
||||
be given access to the dynamic properties sourced from the `ApiServer` bean when those
|
||||
other beans are initialized.
|
||||
|
||||
[[testcontext-ctx-management-dynamic-property-sources-precedence]]
|
||||
== Precedence
|
||||
|
||||
Dynamic properties have higher precedence than those loaded from `@TestPropertySource`,
|
||||
the operating system's environment, Java system properties, or property sources added by
|
||||
the application declaratively by using `@PropertySource` or programmatically. Thus,
|
||||
dynamic properties can be used to selectively override properties loaded via
|
||||
`@TestPropertySource`, system property sources, and application property sources.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.test.context;
|
||||
|
||||
/**
|
||||
* Registrar that is used to add properties with dynamically resolved values to
|
||||
* the {@code Environment} via a {@link DynamicPropertyRegistry}.
|
||||
*
|
||||
* <p>Any bean in a test's {@code ApplicationContext} that implements the
|
||||
* {@code DynamicPropertyRegistrar} interface will be automatically detected and
|
||||
* eagerly initialized before the singleton pre-instantiation phase, and the
|
||||
* {@link #accept} methods of such beans will be invoked with a
|
||||
* {@code DynamicPropertyRegistry} that performs the actual dynamic property
|
||||
* registration on behalf of the registrar.
|
||||
*
|
||||
* <p>This is an alternative to implementing
|
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods in integration
|
||||
* test classes and supports additional use cases that are not possible with a
|
||||
* {@code @DynamicPropertySource} method. For example, since a
|
||||
* {@code DynamicPropertyRegistrar} is itself a bean in the {@code ApplicationContext},
|
||||
* it can interact with other beans in the context and register dynamic properties
|
||||
* that are sourced from those beans. Note, however, that any interaction with
|
||||
* other beans results in eager initialization of those other beans and their
|
||||
* dependencies.
|
||||
*
|
||||
* <h3>Precedence</h3>
|
||||
*
|
||||
* <p>Dynamic properties have higher precedence than those loaded from
|
||||
* {@link TestPropertySource @TestPropertySource}, the operating system's
|
||||
* environment, Java system properties, or property sources added by the
|
||||
* application declaratively by using
|
||||
* {@link org.springframework.context.annotation.PropertySource @PropertySource}
|
||||
* or programmatically. Thus, dynamic properties can be used to selectively
|
||||
* override properties loaded via {@code @TestPropertySource}, system property
|
||||
* sources, and application property sources.
|
||||
*
|
||||
* <h3>Example</h3>
|
||||
*
|
||||
* <p>The following example demonstrates how to implement a
|
||||
* {@code DynamicPropertyRegistrar} as a lambda expression that registers a
|
||||
* dynamic property for the {@code ApiServer} bean. Other beans in the
|
||||
* {@code ApplicationContext} can access the {@code api.url} property which is
|
||||
* dynamically retrieved from the {@code ApiServer} bean — for example,
|
||||
* via {@code @Value("${api.url}")}.
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* class TestConfig {
|
||||
*
|
||||
* @Bean
|
||||
* ApiServer apiServer() {
|
||||
* return new ApiServer();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* DynamicPropertyRegistrar apiServerProperties(ApiServer apiServer) {
|
||||
* return registry -> registry.add("api.url", apiServer::getUrl);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
* @see DynamicPropertySource
|
||||
* @see DynamicPropertyRegistry
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DynamicPropertyRegistrar {
|
||||
|
||||
void accept(DynamicPropertyRegistry registry);
|
||||
|
||||
}
|
||||
|
|
@ -27,22 +27,18 @@ import java.util.function.Supplier;
|
|||
* test classes.
|
||||
*
|
||||
* <p>As of Spring Framework 6.2, a {@code DynamicPropertyRegistry} is also
|
||||
* registered as a singleton bean in the test's {@code ApplicationContext}. This
|
||||
* allows a {@code DynamicPropertyRegistry} to be autowired into a
|
||||
* {@code @Configuration} class or supplied to a {@code @Bean} method as an
|
||||
* argument, making it possible to register a dynamic property from within a test's
|
||||
* {@code ApplicationContext}. For example, a {@code @Bean} method can register
|
||||
* a property whose value is dynamically sourced from the bean that the method
|
||||
* returns. Note that such a {@code @Bean} method can optionally be annotated
|
||||
* with {@code @DynamicPropertySource} to enforce eager initialization of the
|
||||
* bean within the context, thereby ensuring that any dynamic properties sourced
|
||||
* from that bean are available to other singleton beans within the context.
|
||||
* See {@link DynamicPropertySource @DynamicPropertySource} for an example.
|
||||
* supplied to {@link DynamicPropertyRegistrar} beans in the test's
|
||||
* {@code ApplicationContext}, making it possible to register dynamic properties
|
||||
* based on beans in the context. For example, a {@code @Bean} method can return
|
||||
* a {@code DynamicPropertyRegistrar} that registers a property whose value is
|
||||
* dynamically sourced from another bean in the context. See the documentation
|
||||
* for {@code DynamicPropertyRegistrar} for an example.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2.5
|
||||
* @see DynamicPropertySource
|
||||
* @see DynamicPropertyRegistrar
|
||||
*/
|
||||
public interface DynamicPropertyRegistry {
|
||||
|
||||
|
|
|
|||
|
|
@ -24,42 +24,35 @@ import java.lang.annotation.Target;
|
|||
|
||||
/**
|
||||
* {@code @DynamicPropertySource} is an annotation that can be applied to static
|
||||
* methods in integration test classes or to {@code @Bean} methods in test
|
||||
* {@code @Configuration} classes in order to add properties with dynamic values
|
||||
* to the {@code Environment}'s set of {@code PropertySources}.
|
||||
* methods in integration test classes in order to add properties with dynamic
|
||||
* values to the {@code Environment}'s set of {@code PropertySources}.
|
||||
*
|
||||
* <p>Alternatively, dynamic properties can be added to the {@code Environment}
|
||||
* by special beans in the test's {@code ApplicationContext}. See
|
||||
* {@link DynamicPropertyRegistrar} for details.
|
||||
*
|
||||
* <p>This annotation and its supporting infrastructure were originally designed
|
||||
* to allow properties from
|
||||
* <a href="https://www.testcontainers.org/">Testcontainers</a> based tests to be
|
||||
* exposed easily to Spring integration tests. However, this feature may be used
|
||||
* with any form of external resource whose lifecycle is managed outside the
|
||||
* test's {@code ApplicationContext} or with beans whose lifecycle is managed by
|
||||
* the test's {@code ApplicationContext}.
|
||||
* test's {@code ApplicationContext}.
|
||||
*
|
||||
* <p>{@code @DynamicPropertySource}-annotated methods use a
|
||||
* {@code DynamicPropertyRegistry} to add <em>name-value</em> pairs to the
|
||||
* {@code Environment}'s set of {@code PropertySources}. Values are dynamic and
|
||||
* provided via a {@link java.util.function.Supplier} which is only invoked when
|
||||
* the property is resolved. Typically, method references are used to supply values,
|
||||
* as in the example below.
|
||||
* <p>{@code @DynamicPropertySource} methods use a {@link DynamicPropertyRegistry}
|
||||
* to add <em>name-value</em> pairs to the {@code Environment}'s set of
|
||||
* {@code PropertySources}. Values are dynamic and provided via a
|
||||
* {@link java.util.function.Supplier} which is only invoked when the property is
|
||||
* resolved. Typically, method references are used to supply values, as in the
|
||||
* example below.
|
||||
*
|
||||
* <p>Methods in integration test classes that are annotated with
|
||||
* {@code @DynamicPropertySource} must be {@code static} and must accept a single
|
||||
* {@link DynamicPropertyRegistry} argument.
|
||||
*
|
||||
* <p>{@code @Bean} methods annotated with {@code @DynamicPropertySource} may
|
||||
* either accept an argument of type {@code DynamicPropertyRegistry} or access a
|
||||
* {@code DynamicPropertyRegistry} instance autowired into their enclosing
|
||||
* {@code @Configuration} class. Note, however, that {@code @Bean} methods which
|
||||
* interact with a {@code DynamicPropertyRegistry} are not required to be annotated
|
||||
* with {@code @DynamicPropertySource} unless they need to enforce eager
|
||||
* initialization of the bean within the context.
|
||||
* See {@link DynamicPropertyRegistry} for details.
|
||||
* {@code DynamicPropertyRegistry} argument.
|
||||
*
|
||||
* <p>Dynamic properties from methods annotated with {@code @DynamicPropertySource}
|
||||
* will be <em>inherited</em> from enclosing test classes, analogous to inheritance
|
||||
* from superclasses and interfaces.
|
||||
* See {@link NestedTestConfiguration @NestedTestConfiguration} for details.
|
||||
* from superclasses and interfaces. See
|
||||
* {@link NestedTestConfiguration @NestedTestConfiguration} for details.
|
||||
*
|
||||
* <p><strong>NOTE</strong>: if you use {@code @DynamicPropertySource} in a base
|
||||
* class and discover that tests in subclasses fail because the dynamic properties
|
||||
|
|
@ -69,6 +62,7 @@ import java.lang.annotation.Target;
|
|||
* correct dynamic properties.
|
||||
*
|
||||
* <h3>Precedence</h3>
|
||||
*
|
||||
* <p>Dynamic properties have higher precedence than those loaded from
|
||||
* {@link TestPropertySource @TestPropertySource}, the operating system's
|
||||
* environment, Java system properties, or property sources added by the
|
||||
|
|
@ -103,28 +97,11 @@ import java.lang.annotation.Target;
|
|||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The following example demonstrates how to use {@code @DynamicPropertySource}
|
||||
* with a {@code @Bean} method. Beans in the {@code ApplicationContext} can
|
||||
* access the {@code api.url} property which is dynamically retrieved from the
|
||||
* {@code ApiServer} bean.
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* class TestConfig {
|
||||
*
|
||||
* @Bean
|
||||
* @DynamicPropertySource
|
||||
* ApiServer apiServer(DynamicPropertyRegistry registry) {
|
||||
* ApiServer apiServer = new ApiServer();
|
||||
* registry.add("api.url", apiServer::getUrl);
|
||||
* return apiServer;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2.5
|
||||
* @see DynamicPropertyRegistry
|
||||
* @see DynamicPropertyRegistrar
|
||||
* @see ContextConfiguration
|
||||
* @see TestPropertySource
|
||||
* @see org.springframework.core.env.PropertySource
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* 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.test.context.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default {@link DynamicPropertyRegistry} implementation.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
final class DefaultDynamicPropertyRegistry implements DynamicPropertyRegistry {
|
||||
|
||||
final Map<String, Supplier<Object>> valueSuppliers = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
private final boolean lazilyRegisterPropertySource;
|
||||
|
||||
private final Lock propertySourcesLock = new ReentrantLock();
|
||||
|
||||
|
||||
DefaultDynamicPropertyRegistry(ConfigurableEnvironment environment, boolean lazilyRegisterPropertySource) {
|
||||
this.environment = environment;
|
||||
this.lazilyRegisterPropertySource = lazilyRegisterPropertySource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void add(String name, Supplier<Object> valueSupplier) {
|
||||
Assert.hasText(name, "'name' must not be null or blank");
|
||||
Assert.notNull(valueSupplier, "'valueSupplier' must not be null");
|
||||
if (this.lazilyRegisterPropertySource) {
|
||||
ensurePropertySourceIsRegistered();
|
||||
}
|
||||
this.valueSuppliers.put(name, valueSupplier);
|
||||
}
|
||||
|
||||
private void ensurePropertySourceIsRegistered() {
|
||||
MutablePropertySources propertySources = this.environment.getPropertySources();
|
||||
this.propertySourcesLock.lock();
|
||||
try {
|
||||
PropertySource<?> ps = propertySources.get(DynamicValuesPropertySource.PROPERTY_SOURCE_NAME);
|
||||
if (ps == null) {
|
||||
propertySources.addFirst(new DynamicValuesPropertySource(this.valueSuppliers));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.propertySourcesLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,35 +26,32 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} which supports
|
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods and registers a
|
||||
* {@link DynamicPropertyRegistry} as a singleton bean in the container for use
|
||||
* in {@code @Configuration} classes and {@code @Bean} methods.
|
||||
* {@link org.springframework.test.context.DynamicPropertySource @DynamicPropertySource}
|
||||
* methods in test classes and registers a {@link DynamicPropertyRegistrarBeanInitializer}
|
||||
* in the container to eagerly initialize
|
||||
* {@link org.springframework.test.context.DynamicPropertyRegistrar DynamicPropertyRegistrar}
|
||||
* beans.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2.5
|
||||
* @see DynamicPropertiesContextCustomizerFactory
|
||||
* @see DefaultDynamicPropertyRegistry
|
||||
* @see DynamicPropertySourceBeanInitializer
|
||||
* @see DynamicPropertyRegistrarBeanInitializer
|
||||
*/
|
||||
class DynamicPropertiesContextCustomizer implements ContextCustomizer {
|
||||
|
||||
private static final String DYNAMIC_PROPERTY_REGISTRY_BEAN_NAME =
|
||||
DynamicPropertiesContextCustomizer.class.getName() + ".dynamicPropertyRegistry";
|
||||
|
||||
private static final String DYNAMIC_PROPERTY_SOURCE_BEAN_INITIALIZER_BEAN_NAME =
|
||||
DynamicPropertiesContextCustomizer.class.getName() + ".dynamicPropertySourceBeanInitializer";
|
||||
private static final String DYNAMIC_PROPERTY_REGISTRAR_BEAN_INITIALIZER_BEAN_NAME =
|
||||
DynamicPropertiesContextCustomizer.class.getName() + ".dynamicPropertyRegistrarBeanInitializer";
|
||||
|
||||
|
||||
private final Set<Method> methods;
|
||||
|
|
@ -68,29 +65,25 @@ class DynamicPropertiesContextCustomizer implements ContextCustomizer {
|
|||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
ConfigurableEnvironment environment = context.getEnvironment();
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (!(beanFactory instanceof BeanDefinitionRegistry beanDefinitionRegistry)) {
|
||||
throw new IllegalStateException("BeanFactory must be a BeanDefinitionRegistry");
|
||||
}
|
||||
|
||||
DefaultDynamicPropertyRegistry dynamicPropertyRegistry =
|
||||
new DefaultDynamicPropertyRegistry(environment, this.methods.isEmpty());
|
||||
beanFactory.registerSingleton(DYNAMIC_PROPERTY_REGISTRY_BEAN_NAME, dynamicPropertyRegistry);
|
||||
|
||||
if (!beanDefinitionRegistry.containsBeanDefinition(DYNAMIC_PROPERTY_SOURCE_BEAN_INITIALIZER_BEAN_NAME)) {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(DynamicPropertySourceBeanInitializer.class);
|
||||
if (!beanDefinitionRegistry.containsBeanDefinition(DYNAMIC_PROPERTY_REGISTRAR_BEAN_INITIALIZER_BEAN_NAME)) {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(DynamicPropertyRegistrarBeanInitializer.class);
|
||||
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
beanDefinitionRegistry.registerBeanDefinition(
|
||||
DYNAMIC_PROPERTY_SOURCE_BEAN_INITIALIZER_BEAN_NAME, beanDefinition);
|
||||
DYNAMIC_PROPERTY_REGISTRAR_BEAN_INITIALIZER_BEAN_NAME, beanDefinition);
|
||||
}
|
||||
|
||||
if (!this.methods.isEmpty()) {
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
propertySources.addFirst(new DynamicValuesPropertySource(dynamicPropertyRegistry.valueSuppliers));
|
||||
ConfigurableEnvironment environment = context.getEnvironment();
|
||||
DynamicValuesPropertySource propertySource = DynamicValuesPropertySource.getOrCreate(environment);
|
||||
DynamicPropertyRegistry registry = propertySource.dynamicPropertyRegistry;
|
||||
this.methods.forEach(method -> {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
ReflectionUtils.invokeMethod(method, null, dynamicPropertyRegistry);
|
||||
ReflectionUtils.invokeMethod(method, null, registry);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,15 +27,14 @@ import org.springframework.core.annotation.MergedAnnotations;
|
|||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} which supports
|
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods and the
|
||||
* registration of a {@link DynamicPropertyRegistry} as a singleton bean in the
|
||||
* container for use in {@code @Configuration} classes and {@code @Bean} methods.
|
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods in test classes
|
||||
* and {@link org.springframework.test.context.DynamicPropertyRegistrar
|
||||
* DynamicPropertyRegistrar} beans in the container.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.test.context.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryInitializer;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.DynamicPropertyRegistrar;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
|
||||
/**
|
||||
* Internal component which eagerly initializes {@link DynamicPropertyRegistrar}
|
||||
* beans.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
class DynamicPropertyRegistrarBeanInitializer implements BeanFactoryInitializer<ListableBeanFactory>, EnvironmentAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DynamicPropertyRegistrarBeanInitializer.class);
|
||||
|
||||
|
||||
@Nullable
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
if (!(environment instanceof ConfigurableEnvironment configurableEnvironment)) {
|
||||
throw new IllegalArgumentException("Environment must be a ConfigurableEnvironment");
|
||||
}
|
||||
this.environment = configurableEnvironment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ListableBeanFactory beanFactory) {
|
||||
if (this.environment == null) {
|
||||
throw new IllegalStateException("Environment is required");
|
||||
}
|
||||
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
beanFactory, DynamicPropertyRegistrar.class);
|
||||
if (beanNames.length > 0) {
|
||||
DynamicValuesPropertySource propertySource = DynamicValuesPropertySource.getOrCreate(this.environment);
|
||||
DynamicPropertyRegistry registry = propertySource.dynamicPropertyRegistry;
|
||||
for (String name : beanNames) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Eagerly initializing DynamicPropertyRegistrar bean '%s'".formatted(name));
|
||||
}
|
||||
DynamicPropertyRegistrar registrar = beanFactory.getBean(name, DynamicPropertyRegistrar.class);
|
||||
registrar.accept(registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* 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.test.context.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryInitializer;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
|
||||
/**
|
||||
* Internal component which eagerly initializes beans created by {@code @Bean}
|
||||
* factory methods annotated with {@link DynamicPropertySource @DynamicPropertySource}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
class DynamicPropertySourceBeanInitializer implements BeanFactoryInitializer<ListableBeanFactory> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DynamicPropertySourceBeanInitializer.class);
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(ListableBeanFactory beanFactory) {
|
||||
for (String name : beanFactory.getBeanNamesForAnnotation(DynamicPropertySource.class)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Eagerly initializing @DynamicPropertySource bean '%s'".formatted(name));
|
||||
}
|
||||
beanFactory.getBean(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,11 +17,17 @@
|
|||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.SupplierUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -36,9 +42,20 @@ class DynamicValuesPropertySource extends MapPropertySource {
|
|||
|
||||
static final String PROPERTY_SOURCE_NAME = "Dynamic Test Properties";
|
||||
|
||||
final DynamicPropertyRegistry dynamicPropertyRegistry;
|
||||
|
||||
|
||||
DynamicValuesPropertySource() {
|
||||
this(Collections.synchronizedMap(new LinkedHashMap<>()));
|
||||
}
|
||||
|
||||
DynamicValuesPropertySource(Map<String, Supplier<Object>> valueSuppliers) {
|
||||
super(PROPERTY_SOURCE_NAME, Collections.unmodifiableMap(valueSuppliers));
|
||||
this.dynamicPropertyRegistry = (name, valueSupplier) -> {
|
||||
Assert.hasText(name, "'name' must not be null or blank");
|
||||
Assert.notNull(valueSupplier, "'valueSupplier' must not be null");
|
||||
valueSuppliers.put(name, valueSupplier);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -48,4 +65,27 @@ class DynamicValuesPropertySource extends MapPropertySource {
|
|||
return SupplierUtils.resolve(super.getProperty(name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@code DynamicValuesPropertySource} registered in the environment
|
||||
* or create and register a new {@code DynamicValuesPropertySource} in the
|
||||
* environment.
|
||||
*/
|
||||
static DynamicValuesPropertySource getOrCreate(ConfigurableEnvironment environment) {
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
PropertySource<?> propertySource = propertySources.get(PROPERTY_SOURCE_NAME);
|
||||
if (propertySource instanceof DynamicValuesPropertySource dynamicValuesPropertySource) {
|
||||
return dynamicValuesPropertySource;
|
||||
}
|
||||
else if (propertySource == null) {
|
||||
DynamicValuesPropertySource dynamicValuesPropertySource = new DynamicValuesPropertySource();
|
||||
propertySources.addFirst(dynamicValuesPropertySource);
|
||||
return dynamicValuesPropertySource;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("PropertySource with name '%s' must be a DynamicValuesPropertySource"
|
||||
.formatted(PROPERTY_SOURCE_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ import org.junit.platform.suite.api.Suite;
|
|||
@Suite
|
||||
@SelectClasses(
|
||||
value = {
|
||||
DynamicPropertyRegistryIntegrationTests.class,
|
||||
DynamicPropertyRegistrarIntegrationTests.class,
|
||||
DynamicPropertySourceIntegrationTests.class
|
||||
},
|
||||
names = {
|
||||
|
|
|
|||
|
|
@ -29,60 +29,77 @@ import org.springframework.core.env.MutablePropertySources;
|
|||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DynamicPropertyRegistry} bean support.
|
||||
* Integration tests for {@link DynamicPropertyRegistrar} bean support.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
* @see DynamicPropertySourceIntegrationTests
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@TestPropertySource(properties = "api.url: https://example.com/test")
|
||||
class DynamicPropertyRegistryIntegrationTests {
|
||||
@TestPropertySource(properties = "api.url.1: https://example.com/test")
|
||||
class DynamicPropertyRegistrarIntegrationTests {
|
||||
|
||||
private static final String API_URL = "api.url";
|
||||
private static final String API_URL_1 = "api.url.1";
|
||||
private static final String API_URL_2 = "api.url.2";
|
||||
|
||||
|
||||
@Test
|
||||
void customDynamicPropertyRegistryCanExistInApplicationContext(
|
||||
@Autowired DynamicPropertyRegistry dynamicPropertyRegistry) {
|
||||
|
||||
assertThatRuntimeException()
|
||||
.isThrownBy(() -> dynamicPropertyRegistry.add("test", () -> "test"))
|
||||
.withMessage("Boom!");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dynamicPropertySourceOverridesTestPropertySource(@Autowired ConfigurableEnvironment env) {
|
||||
assertApiUrlIsDynamic(env.getProperty(API_URL));
|
||||
assertApiUrlIsDynamic1(env.getProperty(API_URL_1));
|
||||
|
||||
MutablePropertySources propertySources = env.getPropertySources();
|
||||
assertThat(propertySources.size()).isGreaterThanOrEqualTo(4);
|
||||
assertThat(propertySources.contains("Inlined Test Properties")).isTrue();
|
||||
assertThat(propertySources.contains("Dynamic Test Properties")).isTrue();
|
||||
assertThat(propertySources.get("Inlined Test Properties").getProperty(API_URL)).isEqualTo("https://example.com/test");
|
||||
assertThat(propertySources.get("Dynamic Test Properties").getProperty(API_URL)).isEqualTo("https://example.com/dynamic");
|
||||
assertThat(propertySources.get("Inlined Test Properties").getProperty(API_URL_1)).isEqualTo("https://example.com/test");
|
||||
assertThat(propertySources.get("Dynamic Test Properties").getProperty(API_URL_1)).isEqualTo("https://example.com/dynamic/1");
|
||||
assertThat(propertySources.get("Dynamic Test Properties").getProperty(API_URL_2)).isEqualTo("https://example.com/dynamic/2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReceivesDynamicProperty(@Value("${api.url}") String apiUrl) {
|
||||
assertApiUrlIsDynamic(apiUrl);
|
||||
void testReceivesDynamicProperties(@Value("${api.url.1}") String apiUrl1, @Value("${api.url.2}") String apiUrl2) {
|
||||
assertApiUrlIsDynamic1(apiUrl1);
|
||||
assertApiUrlIsDynamic2(apiUrl2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void environmentInjectedServiceCanRetrieveDynamicProperty(@Autowired EnvironmentInjectedService service) {
|
||||
assertApiUrlIsDynamic(service);
|
||||
assertApiUrlIsDynamic1(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInjectedServiceReceivesDynamicProperty(@Autowired ConstructorInjectedService service) {
|
||||
assertApiUrlIsDynamic(service);
|
||||
assertApiUrlIsDynamic1(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setterInjectedServiceReceivesDynamicProperty(@Autowired SetterInjectedService service) {
|
||||
assertApiUrlIsDynamic(service);
|
||||
assertApiUrlIsDynamic1(service);
|
||||
}
|
||||
|
||||
|
||||
private static void assertApiUrlIsDynamic(ApiUrlClient service) {
|
||||
assertApiUrlIsDynamic(service.getApiUrl());
|
||||
private static void assertApiUrlIsDynamic1(ApiUrlClient service) {
|
||||
assertApiUrlIsDynamic1(service.getApiUrl());
|
||||
}
|
||||
|
||||
private static void assertApiUrlIsDynamic(String apiUrl) {
|
||||
assertThat(apiUrl).isEqualTo("https://example.com/dynamic");
|
||||
private static void assertApiUrlIsDynamic1(String apiUrl) {
|
||||
assertThat(apiUrl).isEqualTo("https://example.com/dynamic/1");
|
||||
}
|
||||
|
||||
private static void assertApiUrlIsDynamic2(String apiUrl) {
|
||||
assertThat(apiUrl).isEqualTo("https://example.com/dynamic/2");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -90,16 +107,30 @@ class DynamicPropertyRegistryIntegrationTests {
|
|||
@Import({ EnvironmentInjectedService.class, ConstructorInjectedService.class, SetterInjectedService.class })
|
||||
static class Config {
|
||||
|
||||
// Annotating this @Bean method with @DynamicPropertySource ensures that
|
||||
// this bean will be instantiated before any other singleton beans in the
|
||||
@Bean
|
||||
ApiServer apiServer() {
|
||||
return new ApiServer();
|
||||
}
|
||||
|
||||
// Accepting ApiServer as a method argument ensures that the apiServer
|
||||
// bean will be instantiated before any other singleton beans in the
|
||||
// context which further ensures that the dynamic "api.url" property is
|
||||
// available to all standard singleton beans.
|
||||
@Bean
|
||||
@DynamicPropertySource
|
||||
ApiServer apiServer(DynamicPropertyRegistry registry) {
|
||||
ApiServer apiServer = new ApiServer();
|
||||
registry.add(API_URL, apiServer::getUrl);
|
||||
return apiServer;
|
||||
DynamicPropertyRegistrar apiServerProperties1(ApiServer apiServer) {
|
||||
return registry -> registry.add(API_URL_1, () -> apiServer.getUrl() + "/1");
|
||||
}
|
||||
|
||||
@Bean
|
||||
DynamicPropertyRegistrar apiServerProperties2(ApiServer apiServer) {
|
||||
return registry -> registry.add(API_URL_2, () -> apiServer.getUrl() + "/2");
|
||||
}
|
||||
|
||||
@Bean
|
||||
DynamicPropertyRegistry dynamicPropertyRegistry() {
|
||||
return (name, valueSupplier) -> {
|
||||
throw new RuntimeException("Boom!");
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -120,7 +151,7 @@ class DynamicPropertyRegistryIntegrationTests {
|
|||
|
||||
@Override
|
||||
public String getApiUrl() {
|
||||
return this.env.getProperty(API_URL);
|
||||
return this.env.getProperty(API_URL_1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +160,7 @@ class DynamicPropertyRegistryIntegrationTests {
|
|||
private final String apiUrl;
|
||||
|
||||
|
||||
ConstructorInjectedService(@Value("${api.url}") String apiUrl) {
|
||||
ConstructorInjectedService(@Value("${api.url.1}") String apiUrl) {
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +176,7 @@ class DynamicPropertyRegistryIntegrationTests {
|
|||
|
||||
|
||||
@Autowired
|
||||
void setApiUrl(@Value("${api.url}") String apiUrl) {
|
||||
void setApiUrl(@Value("${api.url.1}") String apiUrl) {
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +23,7 @@ import org.junit.jupiter.api.TestInstance;
|
|||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
|
@ -30,14 +31,16 @@ import org.springframework.core.env.MutablePropertySources;
|
|||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
|
||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DynamicPropertySource @DynamicPropertySource}.
|
||||
* Integration tests for {@link DynamicPropertySource @DynamicPropertySource} and
|
||||
* {@link DynamicPropertyRegistrar} bean support.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @see DynamicPropertyRegistryIntegrationTests
|
||||
* @see DynamicPropertyRegistrarIntegrationTests
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@TestPropertySource(properties = "test.container.ip: test")
|
||||
|
|
@ -46,6 +49,7 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
|||
class DynamicPropertySourceIntegrationTests {
|
||||
|
||||
private static final String TEST_CONTAINER_IP = "test.container.ip";
|
||||
private static final String MAGIC_WORD = "magic.word";
|
||||
|
||||
static {
|
||||
System.setProperty(TEST_CONTAINER_IP, "system");
|
||||
|
|
@ -54,8 +58,12 @@ class DynamicPropertySourceIntegrationTests {
|
|||
static final DemoContainer container = new DemoContainer();
|
||||
|
||||
@DynamicPropertySource
|
||||
static void containerProperties(DynamicPropertyRegistry registry) {
|
||||
static void containerPropertiesIpAddress(DynamicPropertyRegistry registry) {
|
||||
registry.add(TEST_CONTAINER_IP, container::getIpAddress);
|
||||
}
|
||||
|
||||
@DynamicPropertySource
|
||||
static void containerPropertiesPort(DynamicPropertyRegistry registry) {
|
||||
registry.add("test.container.port", container::getPort);
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +73,16 @@ class DynamicPropertySourceIntegrationTests {
|
|||
System.clearProperty(TEST_CONTAINER_IP);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("A custom DynamicPropertyRegistry bean can exist in the ApplicationContext")
|
||||
void customDynamicPropertyRegistryCanExistInApplicationContext(
|
||||
@Autowired DynamicPropertyRegistry dynamicPropertyRegistry) {
|
||||
|
||||
assertThatRuntimeException()
|
||||
.isThrownBy(() -> dynamicPropertyRegistry.add("test", () -> "test"))
|
||||
.withMessage("Boom!");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("@DynamicPropertySource overrides @TestPropertySource and JVM system property")
|
||||
void dynamicPropertySourceOverridesTestPropertySourceAndSystemProperty(@Autowired ConfigurableEnvironment env) {
|
||||
|
|
@ -74,9 +92,11 @@ class DynamicPropertySourceIntegrationTests {
|
|||
assertThat(propertySources.contains("Inlined Test Properties")).isTrue();
|
||||
assertThat(propertySources.contains("systemProperties")).isTrue();
|
||||
assertThat(propertySources.get("Dynamic Test Properties").getProperty(TEST_CONTAINER_IP)).isEqualTo("127.0.0.1");
|
||||
assertThat(propertySources.get("Dynamic Test Properties").getProperty(MAGIC_WORD)).isEqualTo("enigma");
|
||||
assertThat(propertySources.get("Inlined Test Properties").getProperty(TEST_CONTAINER_IP)).isEqualTo("test");
|
||||
assertThat(propertySources.get("systemProperties").getProperty(TEST_CONTAINER_IP)).isEqualTo("system");
|
||||
assertThat(env.getProperty(TEST_CONTAINER_IP)).isEqualTo("127.0.0.1");
|
||||
assertThat(env.getProperty(MAGIC_WORD)).isEqualTo("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -90,6 +110,19 @@ class DynamicPropertySourceIntegrationTests {
|
|||
@Configuration
|
||||
@Import(Service.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
DynamicPropertyRegistrar magicWordProperties() {
|
||||
return registry -> registry.add(MAGIC_WORD, () -> "enigma");
|
||||
}
|
||||
|
||||
@Bean
|
||||
DynamicPropertyRegistry dynamicPropertyRegistry() {
|
||||
return (name, valueSupplier) -> {
|
||||
throw new RuntimeException("Boom!");
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Service {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ abstract class AbstractAotTests {
|
|||
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterImportedConfigTests__TestContext001_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterImportedConfigTests__TestContext001_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext001_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext001_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext001_BeanDefinitions.java",
|
||||
// BasicSpringJupiterSharedConfigTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext002_BeanDefinitions.java",
|
||||
|
|
@ -51,7 +51,7 @@ abstract class AbstractAotTests {
|
|||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext002_ManagementBeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext002_BeanDefinitions.java",
|
||||
// BasicSpringJupiterTests -- not generated b/c already generated for BasicSpringJupiterSharedConfigTests.
|
||||
// BasicSpringJupiterTests.NestedTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext003_BeanDefinitions.java",
|
||||
|
|
@ -63,28 +63,28 @@ abstract class AbstractAotTests {
|
|||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext003_ManagementBeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext003_BeanDefinitions.java",
|
||||
// BasicSpringTestNGTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext004_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext004_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext004_BeanDefinitions.java",
|
||||
// BasicSpringVintageTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext005_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext005_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext005_BeanDefinitions.java",
|
||||
// DisabledInAotRuntimeMethodLevelTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/DisabledInAotRuntimeMethodLevelTests__TestContext006_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/basic/DisabledInAotRuntimeMethodLevelTests__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/DisabledInAotRuntimeMethodLevelTests__TestContext006_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext006_BeanDefinitions.java"
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext006_BeanDefinitions.java"
|
||||
};
|
||||
|
||||
Stream<Class<?>> scan() {
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
|
|||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext001_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext001_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext001_ManagementBeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext001_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext001_BeanDefinitions.java",
|
||||
// BasicSpringJupiterTests -- not generated b/c already generated for BasicSpringJupiterSharedConfigTests.
|
||||
// BasicSpringJupiterTests.NestedTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext002_BeanDefinitions.java",
|
||||
|
|
@ -407,28 +407,28 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
|
|||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext002_ManagementBeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext002_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext002_BeanDefinitions.java",
|
||||
// BasicSpringTestNGTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext003_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext003_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext003_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext003_BeanDefinitions.java",
|
||||
// BasicSpringVintageTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext004_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext004_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext004_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext004_BeanDefinitions.java",
|
||||
// SqlScriptsSpringJupiterTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/jdbc/SqlScriptsSpringJupiterTests__TestContext005_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/jdbc/SqlScriptsSpringJupiterTests__TestContext005_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/jdbc/EmptyDatabaseConfig__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext005_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext005_BeanDefinitions.java",
|
||||
// WebSpringJupiterTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext006_BeanDefinitions.java",
|
||||
|
|
@ -437,14 +437,14 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
|
|||
"org/springframework/test/context/aot/samples/web/WebTestConfiguration__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_Autowiring.java",
|
||||
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext006_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext006_BeanDefinitions.java",
|
||||
// XmlSpringJupiterTests
|
||||
"org/springframework/context/event/DefaultEventListenerFactory__TestContext007_BeanDefinitions.java",
|
||||
"org/springframework/context/event/EventListenerMethodProcessor__TestContext007_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/common/DefaultMessageService__TestContext007_BeanDefinitions.java",
|
||||
"org/springframework/test/context/aot/samples/xml/XmlSpringJupiterTests__TestContext007_ApplicationContextInitializer.java",
|
||||
"org/springframework/test/context/aot/samples/xml/XmlSpringJupiterTests__TestContext007_BeanFactoryRegistrations.java",
|
||||
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext007_BeanDefinitions.java",
|
||||
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext007_BeanDefinitions.java",
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue