How does @ServiceConnection differ from @DynamicPropertySource, and when might you still need the latter?
answer
- DynamicPropertySource = string keys + lambdas
- ServiceConnection = typed ConnectionDetails, declarative
- ConnectionDetails beats properties in auto-config
- Fallback for unsupported/custom properties
- Can coexist
basics
~10 s@DynamicPropertySource manually maps container getters to string property keys. @ServiceConnection does it automatically with a typed ConnectionDetails bean — less code, no typos. You still use @DynamicPropertySource for properties that have no ConnectionDetails mapping.
solid answer
~40 sBoth feed a Testcontainers container's runtime coordinates into the Spring context. @DynamicPropertySource is a static method taking a DynamicPropertyRegistry where you add string-keyed properties (spring.datasource.url, etc.) via supplier lambdas — verbose and easy to mistype. @ServiceConnection is declarative: annotate the container and Boot derives a typed ConnectionDetails bean (JdbcConnectionDetails, KafkaConnectionDetails, ...) that auto-config consumes with higher precedence than properties. Prefer @ServiceConnection: it's typed, handles more than just properties (e.g. SSL bundles), and needs no key knowledge. You fall back to @DynamicPropertySource when the value you need has no ConnectionDetails support — for instance a custom app property, a feature flag derived from the container, or a service Boot has no ConnectionDetailsFactory for. The two can coexist in the same test.
code
java · 19 lines@SpringBootTest
@Testcontainers
class MixedIT {
// Supported type -> declarative, typed
@Container @ServiceConnection
static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16");
// Something with no ConnectionDetails mapping -> still manual
@Container
static GenericContainer<?> wiremock =
new GenericContainer<>("wiremock/wiremock:3").withExposedPorts(8080);
@DynamicPropertySource
static void props(DynamicPropertyRegistry r) {
r.add("app.payments.base-url",
() -> "http://" + wiremock.getHost() + ":" + wiremock.getMappedPort(8080));
}
}go deeper
Should know @ServiceConnection is the newer, less-boilerplate option.
Should contrast typed ConnectionDetails vs string property keys and know both can coexist.
Should state the precedence (ConnectionDetails over properties) and give a concrete fallback case for @DynamicPropertySource.
Should reason about extensibility — writing a custom ConnectionDetailsFactory vs falling back to properties for bespoke services.
## Two ways to feed container coordinates in A Testcontainers container exposes a **random mapped port** and generated credentials only after it starts. Spring must learn these before wiring beans. ### @DynamicPropertySource (Boot 2.4.4+) A `static void` method annotated `@DynamicPropertySource` receives a **`DynamicPropertyRegistry`**. You register **supplier lambdas** keyed by property name: ```java @DynamicPropertySource static void props(DynamicPropertyRegistry r) { r.add("spring.data.redis.host", redis::getHost); r.add("spring.data.redis.port", () -> redis.getMappedPort(6379)); } ``` Suppliers are evaluated lazily, so the container is already started. Downsides: **stringly-typed keys** (a typo silently does nothing), boilerplate per property, and it only sets **properties** — it cannot express richer connection concepts. ### @ServiceConnection (Boot 3.1+) Declarative — one annotation on the container: ```java @Container @ServiceConnection static GenericContainer<?> redis = new GenericContainer<>("redis:7").withExposedPorts(6379); ``` Boot builds a typed **`ConnectionDetails`** (here `RedisConnectionDetails`) that the matching auto-config consumes. Benefits: - **Type-safe**: no property key strings to fat-finger. - **Higher precedence**: `ConnectionDetails` beans override the corresponding `spring.*` properties in auto-config, so the container always wins. - **Richer than properties**: a `ConnectionDetailsFactory` can supply things a single property can't — e.g. SSL bundle wiring for a container. - **Less code**: no per-field lambda. ## Precedence detail Boot's auto-configuration classes are written to look for a `ConnectionDetails` bean first and fall back to properties only when none exists. So if you have both a `@ServiceConnection` and a `spring.datasource.url` property, the **ConnectionDetails from the container wins**. ## When you still need @DynamicPropertySource - The property has **no ConnectionDetails mapping** (a custom `myapp.some-flag`, or a base URL of a service Boot doesn't model). - You run a container type with **no `ConnectionDetailsFactory`** (arbitrary/bespoke service) and can't (or don't want to) write a custom factory. - You need to derive a property from the container in an app-specific way. The two are complementary and can appear in the same test class. ## Gotcha Don't set the same connection with both mechanisms expecting the property to win — for supported types the `@ServiceConnection` `ConnectionDetails` takes precedence, which can surprise you if you're trying to override it via a property.
- If both a @ServiceConnection and a spring.datasource.url property are present, which wins?The ConnectionDetails from @ServiceConnection wins — Boot auto-config prefers a ConnectionDetails bean over the equivalent spring.* property.
- Can @DynamicPropertySource and @ServiceConnection be used together?Yes. Use @ServiceConnection for supported services and @DynamicPropertySource for custom or unsupported properties in the same test.
saying these in an interview costs you the question
- Claiming @ServiceConnection can set any arbitrary custom property (it only covers modeled ConnectionDetails).
- Believing a spring.datasource.url property overrides a @ServiceConnection (it's the other way around).
- Saying @DynamicPropertySource is deprecated/removed (it is not).