DI 컨테이너는 끊임없이 타입 질문에 답해야 한다. "이 빈은 List<Order>인가, List<Customer>인가?", "이 메서드 3번째 파라미터의 어노테이션과 제네릭은?", "후처리기들을 어떤 순서로 적용하지?". JDK의 java.lang.reflect.Type 계층(ParameterizedType, WildcardType, TypeVariable…)은 강력하지만 다루기 까다롭고, 제네릭을 따라 올라가며 실제 클래스로 해석하는 로직을 매번 작성하기 어렵다.
core 패키지 루트는 이 저수준 리플렉션을 사용 가능한 도구로 감싼다. ResolvableType(제네릭 해석), MethodParameter(메서드 인자 명세), ParameterNameDiscoverer(파라미터 이름), Ordered/OrderComparator(순서)가 그 핵심이다. 이들은 spring-beans의 의존성 매칭, MVC의 인자 바인딩, AOP의 어드바이스 정렬 등에서 끝없이 호출된다.
소스: spring-core/src/main/java/org/springframework/core/ (루트)
ResolvableType 제네릭 타입을 탐색/해석 가능한 객체로 캡슐화
MethodParameter 메서드/생성자 + 파라미터 인덱스 명세
GenericTypeResolver 특정 컨텍스트에서 타입 변수 → 실제 타입 해석
ParameterNameDiscoverer 파라미터 이름 복원 (-parameters / Kotlin / ASM)
Ordered / PriorityOrdered 순서 부여 인터페이스
OrderComparator / AnnotationAwareOrderComparator 정렬기
BridgeMethodResolver 제네릭 브리지 메서드 → 원본 메서드 복원
ResolvableType은 하나의 java.lang.reflect.Type을 감싸, 슈퍼타입·인터페이스·제네릭 파라미터를 따라 내비게이션하다가 최종적으로 Class로 resolve할 수 있게 한다. 클래스 주석의 예제가 본질을 보여준다.
// private HashMap<Integer, List<String>> myMap;
ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
t.asMap(); // Map<Integer, List<String>>
t.getGeneric(0).resolve(); // Integer
t.getGeneric(1); // List<String>
t.resolveGeneric(1, 0); // String (1번째 제네릭의 0번째 제네릭)설계상 중요한 두 가지.
NONE상수와 체이닝: 값이 없을 때null대신ResolvableType.NONE을 반환한다. 그래서t.getSuperType().getGeneric(0).resolve()처럼 NPE 걱정 없이 연쇄 호출할 수 있다.- 캐시:
ConcurrentReferenceHashMap기반 정적 캐시(cache, 초기 256)로 동일 타입의 재계산을 피한다. 컨테이너가 같은 타입을 수없이 질의하므로 결정적이다.
진입점은 출처별로 다양하다: forClass, forField, forMethodParameter, forMethodReturnType, forConstructorParameter, forInstance, forClassWithGenerics(List.class, String.class)(명시적 제네릭 부여).
forField/forMethodParameter/forClass …
│
▼
ResolvableType (type + 제네릭 컨텍스트 + VariableResolver)
│ getGeneric(i) / getSuperType() / asCollection() / asMap()
▼ (내비게이션은 또 다른 ResolvableType 반환)
│
▼ resolve()
java.lang.Class<?> (해석 실패 시 null)
isAssignableFrom(other)는 제네릭까지 고려한 할당 가능성을 판단한다 — List<? extends Number>가 List<Integer>를 받아들이는지 같은 판정이 가능해, 제네릭 인지 의존성 주입의 기반이 된다.
MethodParameter는 Method 또는 Constructor + 파라미터 인덱스(반환 타입은 -1) + 중첩 레벨을 묶은 값 객체다. 메서드 인자 하나에 대한 "주소"로서, 파라미터 타입/제네릭/어노테이션을 한 곳에서 얻게 한다. MVC의 HandlerMethodArgumentResolver, 빈 주입 지점 기술 등에서 인자를 식별·전달하는 공통 화폐다. core.annotation의 SynthesizingMethodParameter 하위 클래스는 여기에 @AliasFor 어노테이션 합성을 더한다(5장).
자바는 컴파일 시 -parameters 플래그가 없으면 파라미터 이름을 바이트코드에 남기지 않는다. 그런데 @PathVariable Long id처럼 이름 기반 바인딩이 필요하다. ParameterNameDiscoverer는 이 이름을 복원하는 전략 인터페이스다.
DefaultParameterNameDiscoverer (여러 전략을 순서대로 시도)
├─ StandardReflectionParameterNameDiscoverer (-parameters 메타데이터)
├─ (Kotlin 존재 시) KotlinReflectionParameterNameDiscoverer
└─ PrioritizedParameterNameDiscoverer 로 묶어 첫 성공 채택
getParameterNames(method)는 이름을 못 찾으면 null(혹은 일부 원소가 null인 배열)을 반환한다 — 호출자는 폴백을 준비해야 한다.
Spring은 후처리기·필터·어드바이스를 정렬하기 위해 단순한 정수 순서 모델을 쓴다.
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; // 가장 먼저
int LOWEST_PRECEDENCE = Integer.MAX_VALUE; // 가장 나중
int getOrder();
}- 값이 작을수록 우선순위가 높다(먼저 적용).
PriorityOrdered는Ordered의 특수형으로, "일반Ordered보다도 먼저 처리되어야 하는" 1급 시민을 표시한다. 컨테이너는PriorityOrdered묶음을 일반Ordered묶음보다 앞서 처리한다.OrderComparator가 이 규칙으로 정렬하고,AnnotationAwareOrderComparator는 인터페이스 미구현 객체라도@Order어노테이션(또는@Priority)을 읽어 순서를 부여한다.
ResolvableType.resolve(): 제네릭 내비게이션의 종착점. 가능한 한 구체Class로 환원하고, 미지의 타입 변수면null(또는 제공된 fallback)을 돌려준다.ResolvableType.forInstance(obj): 객체가ResolvableTypeProvider를 구현하면 그가 보고하는 정밀 타입을, 아니면 런타임 클래스를 사용한다 — 팩토리 빈이 자기 산출 타입을 정확히 알릴 수 있는 훅.GenericTypeResolver.resolveTypeArgument(clazz, genericIfc): "이 구현체가Converter<?, ?>의 S/T로 무엇을 주는가"를 푼다. 2장에서 컨버터 시그니처를 추출할 때 바로 이 계열을 쓴다.BridgeMethodResolver.findBridgedMethod(method): 컴파일러가 제네릭 때문에 생성한 브리지 메서드를 만나면 실제 사용자 메서드로 되돌려, 어노테이션/파라미터를 올바른 곳에서 읽게 한다.
- null 회피 + 캐시:
ResolvableType.NONE체이닝과 정적 캐시는 "타입 질의가 뜨겁다"는 현실에 대한 답이다. - 명세 객체 패턴:
MethodParameter는 인자를 1급 객체로 만들어 레이어 간 전달을 단순화한다. - 전략 합성:
ParameterNameDiscoverer는 여러 소스를 순서대로 시도하는 합성 전략으로, JVM/Kotlin 환경 차이를 흡수한다. - 순서 인터페이스 + 어노테이션 이중화:
Ordered구현과@Order어노테이션 두 경로를AnnotationAwareOrderComparator가 통합한다.
core 루트의 타입·리플렉션 도구들은 JDK 리플렉션의 날것을 컨테이너가 쓰기 좋은 형태로 다듬는다. ResolvableType은 제네릭을 안전하게 걷고 캐시하며 클래스로 환원하고, MethodParameter는 인자를 명세 객체로 표준화하며, ParameterNameDiscoverer는 사라진 이름을 복원한다. Ordered/PriorityOrdered와 그 비교기는 프레임워크 전반의 "처리 순서"를 단순한 정수 규약으로 통일한다.