skip to content

How does @ServiceConnection differ from @DynamicPropertySource, and when might you still need the latter?

level: middleimportance: should knowfreq 45%

answer

  1. DynamicPropertySource = string keys + lambdas
  2. ServiceConnection = typed ConnectionDetails, declarative
  3. ConnectionDetails beats properties in auto-config
  4. Fallback for unsupported/custom properties
  5. 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 s

Both 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
java
@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

for a junior

Should know @ServiceConnection is the newer, less-boilerplate option.

for a middle

Should contrast typed ConnectionDetails vs string property keys and know both can coexist.

for a senior

Should state the precedence (ConnectionDetails over properties) and give a concrete fallback case for @DynamicPropertySource.

for a principal

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).

context