81 lines
8.4 KiB
Markdown
81 lines
8.4 KiB
Markdown
---
|
|
title: "Spring Boot Exit Code Mechanism — ExitCodeGenerator, ExitCodeExceptionMapper, startup failure path"
|
|
source_type: official-doc
|
|
url: https://docs.spring.io/spring-boot/reference/features/spring-application.html#features.spring-application.application-exit
|
|
archive_url:
|
|
related_branches: [feature-migration-startup-contract]
|
|
related_projects: [ca-skeleton]
|
|
tags: [spring-boot, exit-code, startup-failure, ExitCodeGenerator, ExitCodeExceptionMapper]
|
|
created: 2026-06-09
|
|
---
|
|
|
|
# Spring Boot Exit Code Mechanism — ExitCodeGenerator, ExitCodeExceptionMapper, startup failure path
|
|
|
|
> Layer: `raw/official-docs/` — Spring Boot 공식 참조 문서 + spring-boot-3.4.0-sources.jar 직독.
|
|
|
|
## Parent / 활용 branch (필수)
|
|
|
|
| Branch | 이 자료가 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/branch-notes/feature-migration-startup-contract]] | D7: startup exit code 표준(78/70/71/72)의 Spring Boot 측 메커니즘 — ExitCodeExceptionMapper가 context refresh 실패 시 실제로 호출되는지, 기본 exit code가 무엇인지 |
|
|
|
|
## 출처 / Source
|
|
|
|
- 원본 URL: https://docs.spring.io/spring-boot/reference/features/spring-application.html#features.spring-application.application-exit
|
|
- ExitCodeExceptionMapper javadoc: https://docs.spring.io/spring-boot/api/java/org/springframework/boot/ExitCodeExceptionMapper.html
|
|
- 소스코드 직독: spring-boot-3.4.0-sources.jar, `org/springframework/boot/SpringApplication.java` (로컬 Gradle 캐시: `/home/donghyeon/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/3.4.0/`)
|
|
- 저자 / 조직: Phillip Webb, Dave Syer (Spring Boot 핵심 기여자)
|
|
- 마지막 확인일: 2026-06-09
|
|
|
|
## 왜 저장했는지 / Why archived
|
|
|
|
D7 결정(startup exit code = 78/70/71/72)을 구현할 때 Spring Boot가 제공하는 exit code 메커니즘이 실제로 startup failure 시 작동하는지 확인하기 위해 수집. ExitCodeExceptionMapper가 context refresh 실패 시점에 호출 가능한지가 핵심 질문.
|
|
|
|
## 핵심 인용 / Key quotes (verbatim)
|
|
|
|
> [§Application Exit] "In addition, beans may implement the `ExitCodeGenerator` interface if they wish to return a specific exit code when `SpringApplication.exit()` is called. This exit code can then be passed to `System.exit()` to return it as a status code."
|
|
|
|
> [§Application Exit] "Also, the `ExitCodeGenerator` interface may be implemented by exceptions. When such an exception is encountered, Spring Boot returns the exit code provided by the implemented `getExitCode()` method."
|
|
|
|
> [§Application Exit] "If there is more than one `ExitCodeGenerator`, the first non-zero exit code that is generated is used. To control the order in which the generators are called, additionally implement the `Ordered` interface or use the `@Order` annotation."
|
|
|
|
> [SpringApplication.java:896-899, source] `private int getExitCodeFromMappedException(ConfigurableApplicationContext context, Throwable exception) { if (context == null || !context.isActive()) { return 0; } ... }`
|
|
|
|
> [SpringApplication.java:1412, source] `exitCode = (exitCode != 0) ? exitCode : 1;`
|
|
|
|
## Claims Extracted / 추출된 주장
|
|
|
|
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
|
|---|---|---|---|---|---|
|
|
| SB-EXIT-C1 | ExitCodeGenerator interface를 implements한 bean은 `SpringApplication.exit()` 호출 시 지정한 exit code를 반환한다 | [§Application Exit] "beans may implement the `ExitCodeGenerator` interface if they wish to return a specific exit code when `SpringApplication.exit()` is called" | `official-vendor-doc` | Spring Boot 1.0+ 모든 버전 | `SpringApplication.exit()`가 호출되어야 작동함. main()에서 `System.exit(SpringApplication.exit(...))` 패턴이 없으면 이 경로가 실행되지 않는다 |
|
|
| SB-EXIT-C2 | 예외 클래스 자체가 ExitCodeGenerator를 구현하면, 그 예외가 발생할 때 해당 exit code가 반환된다 | [§Application Exit] "the `ExitCodeGenerator` interface may be implemented by exceptions. When such an exception is encountered, Spring Boot returns the exit code provided by the implemented `getExitCode()` method" | `official-vendor-doc` | Spring Boot 3.x startup exception chain 내 어떤 위치에서든 작동 (context 의존 없음) | ExitCodeExceptionMapper와 달리 context active 상태 불필요 — 단, 예외 클래스를 직접 수정 가능해야 함 |
|
|
| SB-EXIT-C3 | ExitCodeExceptionMapper는 `context == null` 또는 `!context.isActive()` 이면 조회되지 않는다 (exit code 0 반환) | [SpringApplication.java:896-899] `if (context == null \|\| !context.isActive()) { return 0; }` | `official-vendor-doc` (소스코드 직독) | Spring Boot 3.4.0. context refresh 실패(BeanCreationException 등)는 isActive()=false에 해당함 | ExitCodeExceptionMapper bean이 등록된 경우라도, env 누락처럼 context 생성 이전 실패에는 해당 bean이 조회되지 않음을 증명 |
|
|
| SB-EXIT-C4 | startup 실패 시 exit code 결정 순서: (1) ExitCodeExceptionMapper (context active 필요), (2) 예외의 ExitCodeGenerator 구현, (3) 둘 다 0이면 최종 fallback = 1 | [SpringApplication.java:888-893, 1412] `getExitCodeFromMappedException` → `getExitCodeFromExitCodeGeneratorException` → `exitCode = (exitCode != 0) ? exitCode : 1` | `official-vendor-doc` (소스코드 직독) | Spring Boot 3.4.0 startup failure path (handleRunFailure → handleExitCode → getExitCodeFromException) | 이 순서는 Spring Boot 버전마다 다를 수 있음. 3.4.0 소스 직독 기준. |
|
|
| SB-EXIT-C5 | SpringBootExceptionHandler가 UncaughtExceptionHandler로 등록되어 있어, 커스텀 exit code가 실제로 JVM System.exit()로 전파된다 | [SpringBootExceptionHandler.java:49,63] `void registerExitCode(int exitCode) { this.exitCode = exitCode; }` + `if (this.exitCode != 0) { System.exit(this.exitCode); }` | `official-vendor-doc` (소스코드 직독) | Spring Boot 3.4.0 main thread uncaughtException path | 커스텀 exit code가 등록되어야 이 경로가 실행됨. 등록이 0이면 System.exit()가 호출되지 않고 예외가 전파됨 |
|
|
| SB-EXIT-C6 | Spring Boot 공식 문서는 특정 숫자 exit code (78, 70, 71, 72)를 권장하거나 정의하지 않는다 | [§Application Exit] 예시 코드: `return () -> 42;` — 숫자 선택은 애플리케이션 구현자의 책임 | `official-vendor-doc` | 모든 Spring Boot 버전 | 어떤 숫자를 exit code로 사용해야 하는지는 공식이 규정하지 않음 — sysexits(3) 같은 외부 컨벤션을 따르는 것은 구현자 결정 |
|
|
|
|
## Usage Boundaries / 적용 경계
|
|
|
|
- 이 자료가 직접 증명하는 것:
|
|
- `SB-EXIT-C3`: ExitCodeExceptionMapper bean은 context refresh 실패 시 호출되지 않음 (context.isActive() = false 조건)
|
|
- `SB-EXIT-C2`: 예외 클래스 자체가 ExitCodeGenerator를 구현하면 context 상태 무관하게 exit code 적용됨
|
|
- `SB-EXIT-C4`: 커스텀 exit code 없을 때 기본 fallback = 1
|
|
- `SB-EXIT-C5`: SpringBootExceptionHandler를 통해 JVM 실제 exit code로 전파됨
|
|
- 이 자료가 증명하지 않는 것:
|
|
- 어떤 숫자를 exit code로 사용해야 하는지 (78, 70, 71, 72 등) — Spring Boot는 숫자 규약을 정의하지 않음 (SB-EXIT-C6)
|
|
- Kubernetes가 이 exit code를 어떻게 처리하는지
|
|
- ExitCodeExceptionMapper bean이 migration failure처럼 context active 상태에서 발생하는 실패에 작동하는지 — 소스 분석에 따르면 작동하지만 ca-tmpl integration test 검증 필요
|
|
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
|
- env 누락 실패가 실제로 context == null 또는 !isActive() 경로를 타는지 ca-tmpl에서 직접 확인
|
|
- migration failure(Flyway ApplicationRunner)에서 ExitCodeExceptionMapper vs ExitCodeGenerator 중 어느 것이 더 신뢰할 수 있는지 테스트
|
|
|
|
## 메모 / Notes
|
|
|
|
- ExitCodeExceptionMapper는 graceful shutdown (`SpringApplication.exit()` 호출)과 ApplicationRunner 실패 시 잘 작동하지만, env 누락처럼 context 생성 이전 실패에는 ExitCodeGenerator 구현이 유일한 방법.
|
|
- Spring Boot가 exit code 숫자 규약을 정의하지 않으므로, 78/70/71/72는 sysexits(3) BSD 컨벤션을 따르는 ca-tmpl 내부 결정임.
|
|
|
|
## Related / 관련
|
|
|
|
- [[raw/official-docs/sysexits-bsd-exit-code-convention]] — BSD sysexits(3) 컨벤션 (EX_CONFIG=78, EX_SOFTWARE=70 등)
|
|
- [[raw/branch-notes/feature-migration-startup-contract]] — D7 결정 (UNSUPPORTED_DECISION 라벨 해소 대상)
|