Compare @DynamicPropertySource with @ServiceConnection. When would you choose each?
answer
- ServiceConnection = auto via ConnectionDetails bean
- DynamicPropertySource = manual, name each key
- ServiceConnection only supported types
- DynamicPropertySource = escape hatch / any property
- they can coexist
basics
~20 s@ServiceConnection auto-wires connection properties for supported Testcontainers (like PostgreSQLContainer) with zero code. @DynamicPropertySource is the manual way: you write a static method mapping each property yourself. Use @ServiceConnection for supported containers; @DynamicPropertySource for anything custom.
solid answer
~40 sBoth feed live container values into the test's configuration, but at different levels. @ServiceConnection (Spring Boot 3.1+) is placed on a static container field; Boot recognizes the container type via a ConnectionDetails factory and automatically contributes the right beans/properties (JDBC URL, credentials, Kafka bootstrap servers, etc.) — no property names to write, and it even bypasses the datasource-URL-based auto-config. @DynamicPropertySource is lower-level and explicit: you enumerate each property key and supplier yourself. Choose @ServiceConnection when the container is supported and you want minimal, correct wiring. Choose @DynamicPropertySource when: the container/service isn't supported (custom images, WireMock, a bespoke service), you need to set arbitrary properties not covered by ConnectionDetails, or you must override specific values. Many codebases mix both. @ServiceConnection is preferred as the default; @DynamicPropertySource remains the escape hatch.
code
java · 21 lines@SpringBootTest
@Testcontainers
class PaymentServiceIT {
// Auto-wires spring.datasource.* — no property names needed
@Container
@ServiceConnection
static final PostgreSQLContainer<?> POSTGRES =
new PostgreSQLContainer<>("postgres:16");
// Manual: an unsupported/custom property still needs @DynamicPropertySource
@Container
static final GenericContainer<?> WIREMOCK =
new GenericContainer<>("wiremock/wiremock:3").withExposedPorts(8080);
@DynamicPropertySource
static void externalApi(DynamicPropertyRegistry registry) {
registry.add("app.payments.base-url",
() -> "http://" + WIREMOCK.getHost() + ":" + WIREMOCK.getMappedPort(8080));
}
}go deeper
May only know one of the two; acceptable to know just @DynamicPropertySource.
Should know @ServiceConnection is the auto alternative for supported containers.
Should explain the ConnectionDetails mechanism and give a clear decision rule.
Should discuss writing a custom ContainerConnectionDetailsFactory and coexistence trade-offs.
## Two mechanisms, same goal Both exist to inject **runtime-discovered connection info** (host, random port, URL, credentials) from a live dependency into a Spring Boot test. They differ in **abstraction level** and **automation**. ### @DynamicPropertySource (Spring Framework, since 5.2.5) - A `static` method + `DynamicPropertyRegistry`. - You **explicitly name each property** and provide a supplier. - Works for **any** property and **any** value source — not limited to known container types. - More boilerplate; you must know the correct Spring property keys (`spring.datasource.url`, `spring.data.redis.host`, …). ### @ServiceConnection (Spring Boot, since 3.1) - An annotation on a `static` container field (used with `@Testcontainers`, or on `@Bean` container definitions in a `@TestConfiguration`). - Boot maps the container to a **`ConnectionDetails`** bean via a registered `ContainerConnectionDetailsFactory` (e.g. `JdbcConnectionDetails`, `KafkaConnectionDetails`, `RedisConnectionDetails`). - **No property names** — auto-configuration consumes the `ConnectionDetails` bean directly, which also means it **takes precedence over `spring.datasource.url`-style properties** and works even when those aren't set. - Only works for **supported container types** (Postgres, MySQL, MariaDB, MongoDB, Redis, Kafka, RabbitMQ, Cassandra, Elasticsearch, Neo4j, etc.). ## Decision guide | Situation | Prefer | |---|---| | Standard supported container (Postgres, Kafka, Redis…) | `@ServiceConnection` | | Custom/unsupported image or non-container service (WireMock, LocalStack endpoint you map manually) | `@DynamicPropertySource` | | Need to set an arbitrary property (feature flag, custom URL) | `@DynamicPropertySource` | | Want least boilerplate and correct-by-default wiring | `@ServiceConnection` | | Need to override one value that auto-config would otherwise set | `@DynamicPropertySource` (or a properties file) | ## Can they coexist? Yes. A common pattern: `@ServiceConnection` on the Postgres container for the datasource, plus a `@DynamicPropertySource` method for a few extra app-specific properties (e.g. an external API base URL pointing at a WireMock/random port). They're complementary. ## Subtle points - `@ServiceConnection` works through **ConnectionDetails beans**, so it integrates cleanly with Boot's auto-config and testcontainers `@ImportTestcontainers`/`@TestConfiguration` styles. - `@DynamicPropertySource` operates purely at the **Environment/property** layer — it doesn't know about container types. - Because both add high-precedence config, don't set the *same* property both ways expecting a merge — pick one owner per property. - For custom `@ServiceConnection` support you can implement your own `ContainerConnectionDetailsFactory`; when that's too much, `@DynamicPropertySource` is the pragmatic choice.
- Does @ServiceConnection require you to know the Spring property key like spring.datasource.url?No — it contributes a ConnectionDetails bean that auto-config consumes directly, so no property key is written and it works even without the url property.
- How would you add @ServiceConnection-style support for an unsupported container?Implement a custom ContainerConnectionDetailsFactory producing the appropriate ConnectionDetails; if that's overkill, fall back to @DynamicPropertySource.
saying these in an interview costs you the question
- Claiming @ServiceConnection works for any arbitrary container/property.
- Saying @DynamicPropertySource is deprecated in favor of @ServiceConnection (it isn't — it's the general escape hatch).
- Thinking @ServiceConnection sets spring.datasource.url as a property (it uses ConnectionDetails beans instead).