Files
llm-wiki/raw/errors/spring-boot-record-multi-constructor-no-default-constructor-2026-06-12.md

3.7 KiB

title, source_type, status, created, tags
title source_type status created tags
Spring Boot record @ConfigurationProperties — 보조 생성자 추가 시 No default constructor found error-note raw 2026-06-12
spring-boot
configuration-properties
record
constructor-binding

Spring Boot record @ConfigurationProperties — 보조 생성자 추가 시 No default constructor found

Parent

raw/branch-notes/feature-domain-event-outbox-contract


증상

OutboundHttpSettings record 에 보조 6-arg 생성자를 추가한 뒤 ApplicationContextRunner@ConfigurationProperties 바인딩 테스트를 실행하면:

org.springframework.beans.factory.BeanCreationException:
  Error creating bean with name 'app.outbound.http-dev.caskeleton.adapter.outbound.httpclient.OutboundHttpSettings':
  Failed to instantiate [...OutboundHttpSettings]: No default constructor found
Caused by: org.springframework.beans.BeanInstantiationException:
  Failed to instantiate [...OutboundHttpSettings]: No default constructor found
Caused by: java.lang.NoSuchMethodException: ...OutboundHttpSettings.<init>()

기존 6-arg 단일 생성자 record 에서는 동일 테스트가 통과했음.


원인

Spring Boot 3.x 는 @ConfigurationProperties record 에 생성자가 정확히 하나일 때만 canonical constructor binding 을 자동 감지한다. 생성자가 2개 이상(canonical + 보조)이면 Spring 은 단일 생성자 record 특수 경로를 포기하고 일반 JavaBean 경로로 fallback — JavaBean 경로는 no-arg 생성자를 찾다 실패.

핵심 규칙: record 에 생성자가 여러 개이면 바인딩 대상 생성자를 명시해야 한다.


해결

바인딩에 사용할 canonical compact constructor 에 @ConstructorBinding 어노테이션을 추가한다.

import org.springframework.boot.context.properties.bind.ConstructorBinding;

@ConfigurationProperties(prefix = "app.outbound.http")
public record OutboundHttpSettings(
        Duration connectTimeout, ...,
        Retry retry, CircuitBreaker circuitBreaker) {

    @ConstructorBinding           // ← 다중 생성자 record 에서 바인딩 대상 명시
    public OutboundHttpSettings {
        // compact constructor body (validation)
    }

    /** 보조 생성자 — 기존 6-arg 호출부 무변경 유지 */
    public OutboundHttpSettings(Duration connectTimeout, ..., DataSize responseSizeLimit) {
        this(connectTimeout, ..., responseSizeLimit, null, null);
    }
}

import 주의: org.springframework.boot.context.properties.bind.ConstructorBinding (Spring Boot 3.x). Spring Boot 2.x 의 org.springframework.boot.context.properties.ConstructorBinding 은 deprecated.


재현 조건

  • Spring Boot 3.x @ConfigurationProperties record
  • record 에 canonical constructor 외에 보조 생성자가 1개 이상 존재
  • ApplicationContextRunner 또는 @SpringBootTest@EnableConfigurationProperties 바인딩

단일 생성자 record 에서는 @ConstructorBinding 없이도 동작.


검증 방법

cd src && ./gradlew :adapter-outbound:test --tests '*OutboundHttpSettingsTest' --console=plain

nested_settings_bind_from_application_context_runner() + settings_bind_from_application_context_runner() 모두 PASS.


Claims To Verify

Claim Why uncertain Status
Spring Boot 3.4 에서 단일 생성자 record 는 @ConstructorBinding 없이 바인딩됨 실측 확인(단일 → 보조 추가 시 실패) locally-verified
org.springframework.boot.context.properties.bind.ConstructorBinding 이 3.x SSOT import Spring Boot 3.4 릴리즈 노트 미확인 — 기존 code 에 해당 어노테이션 미사용 needs-confirmation