spring-core-test는 다른 모듈처럼 "런타임 기능"을 제공하지 않는다. 이 모듈의 산출물(JAR)은 테스트 컴파일 클래스패스와 -javaagent로만 쓰인다. 그래서 책임을 이해하려면 "AOT 처리 결과를 어떻게 검증하는가?"라는 질문에서 출발해야 한다. 이 챕터는 네 개 패키지가 어떤 역할로 나뉘는지, 그리고 그것이 어떻게 두 개의 큰 축으로 묶이는지를 먼저 조감한다.
AOT(Ahead-Of-Time)가 만든 산출물을 검증한다
│
┌─────────────────────────┴──────────────────────────┐
│ │
[축 1] 생성된 코드가 [축 2] 런타임 메타데이터(힌트)가
컴파일·동작하는가? 누락되지 않았는가?
│ │
core.test.tools aot.agent
(TestCompiler) (RuntimeHintsAgent)
│ │
aot.test.generate aot.test.agent
(AOT GeneratedFiles → TestCompiler 어댑터) (RuntimeHintsRecorder + Assert)
- 축 1 — 동적 컴파일: AOT 프로세서는 빈 등록 코드, 프록시, 리플렉션 등록 코드를
.java소스로 생성한다. 테스트는 그 소스를 실제javac로 컴파일해 "정말 빌드되는지", "로딩하면 의도대로 빈을 만드는지"를 확인해야 한다.TestCompiler가 이걸 디스크 없이 메모리 안에서 해낸다. → 02, 03장. - 축 2 — 힌트 검증: GraalVM 네이티브 이미지는 리플렉션·리소스·프록시를
RuntimeHints로 사전 등록해야만 동작한다. 등록을 빠뜨리면 네이티브에서만 터진다. Java Agent가 바이트코드를 계측해 "실행 중 실제로 쓰인 호출"을 기록하고, 설정한 힌트가 그걸 전부 덮는지 대조한다. → 05장.
두 축을 잇는 부속이 aot.test.generate(생성 파일을 컴파일러 입력으로 변환)와 보조 유틸 **MockSpringFactoriesLoader**다. → 04장.
| 패키지 | 핵심 타입 | 책임 |
|---|---|---|
core.test.tools |
TestCompiler, Compiled, SourceFile |
자바 소스/리소스/클래스를 메모리에서 컴파일하고 결과를 클래스로 로딩 |
core.test.tools (격리) |
@CompileWithForkedClassLoader |
컴파일된 클래스가 테스트 클래스로더를 오염시키지 않도록 포크된 로더에서 실행 |
aot.test.generate |
TestGenerationContext, CompilerFiles |
AOT GenerationContext/GeneratedFiles를 TestCompiler 입력으로 변환 |
core.test.io.support |
MockSpringFactoriesLoader |
spring.factories 로딩을 코드로 흉내내는 테스트용 목 |
aot.agent |
RuntimeHintsAgent, InstrumentedMethod |
바이트코드 계측으로 리플렉션/리소스 호출 기록 (deprecated 7.0) |
aot.test.agent |
RuntimeHintsRecorder, RuntimeHintsInvocationsAssert |
기록된 호출을 RuntimeHints와 대조하는 테스트 API |
aot.agent와 aot.test.agent의 공개 진입점들은 7.0에서 @Deprecated(since = "7.0", forRemoval = true)로 표시되었다. 권장 대체재는 GraalVM JVM 플래그다.
-XX:MissingRegistrationReportingMode=Warn (누락 힌트를 경고)
-XX:MissingRegistrationReportingMode=Exit (누락 시 종료)
RuntimeHintsAgent.java 클래스 주석에 그 의도가 명시되어 있다. 즉, 바이트코드를 계측해 우리가 직접 호출을 추적하던 방식은, GraalVM 런타임이 직접 누락을 보고해주는 방식으로 옮겨가는 중이다. 그래도 코드는 7.1에 그대로 남아 있고 설계가 흥미로워, 이 책에서는 동작 원리를 5장에서 다룬다.
- 메인 코드 / 테스트 코드의 분리를 깬다(의도적으로): 이 모듈은
src/main/java에 있지만 사실상 테스트 인프라다. 그래서 JUnit·AssertJ·qdox 의존을optional/compileOnly로 둬, 이 모듈을 쓰는 쪽이 필요한 것만 끌어쓰게 한다(spring-core-test.gradle). - JAR 매니페스트가 곧 진입점: 빌드 스크립트가
Premain-Class: org.springframework.aot.agent.RuntimeHintsAgent와Can-Redefine-Classes: true를 매니페스트에 박는다. 그래서 이 JAR 자체를-javaagent로 붙일 수 있다.
spring-core-test는 "AOT가 만든 것을 검증한다"는 한 가지 목표를, **생성 코드를 직접 컴파일해보는 축(TestCompiler)**과 **런타임 힌트 누락을 추적하는 축(RuntimeHintsAgent)**으로 나눠 푼다. 나머지 패키지는 이 두 축에 입력을 공급하는 어댑터와 목이다. 다음 장부터 각 축을 코드 레벨로 파고든다.