Spring에서 메서드 파라미터에 붙은 어노테이션을 이용하여 객체를 주입 방식을 공부해보았다.
2. 목표 설명
@Princiapl SessionUser user
처럼 어노테이션 기반으로 파라미터에 객체를 자동 주입
- 파라미터별 어노테이션을 리플렉션으로 읽고 처리
- 타입(Class)을 기반으로 객체를 동적으로 생성하거나 가져오기
3. 핵심 개념
getParameterAnnotations vs getParameterTypes
Annotation[][] paramAnnotations
: [파라미터 개수][각 파라미터의 어노테이션 수]- 파라미터에 여러 어노테이션을 붙일 수 있기 때문에 2차원 배열로 정보를 저장해야 한다.
- 예시
java
복사편집
[
[@A], // 첫 번째 파라미터의 어노테이션들
[@B, @C] // 두 번째 파라미터의 어노테이션들
]
Class<?>[] paramTypes
: 각 파라미터가 어떤 타입인지 나타낸다
→ 이 두 가지 정보를 조합하면 어노테이션 + 타입 기반으로 파라미터 주입 로직을 구현할 수 있다
4. instanceof란?
if (annotation instanceof Princiapl)
- 자바의 연산자
- 해당 인스턴스가 특정 클래스(또는 인터페이스)의 객체인지 검사할 때 사용
annotation
은Annotation
타입(부모 타입)이기 때문에 실제로 어떤 어노테이션인지 알 수 없다
- 그래서 "이 어노테이션이
@Princiapl
어노테이션인지?" 를 검사하는 것
boolean
값(true
또는false
)을 반환함
💡 이건 클래스 타입 비교에 쓰는 일종의 "조건 필터"라고 생각하면 된다
5. 파라미터 기반 주입 로직 전체 흐름
for (int i = 0; i < paramAnnotations.length; i++) {
for (Annotation annotation : paramAnnotations[i]) {
if (annotation instanceof Princiapl) {
argsToPass[i] = SessionUser.getInstance();
}
}
if (argsToPass[i] == null) {
if (paramTypes[i] == Model.class) {
argsToPass[i] = Model.getInstance();
}
}
}
- 각 파라미터마다 붙은 어노테이션을 검사하여
@Princiapl
이 있으면 해당 인덱스 위치에SessionUser
싱글톤 인스턴스를 넣어준다
- 만약
@Princiapl
이 없고 아직 값이 할당되지 않았다면, 파라미터 타입이Model
인 경우Model
인스턴스를 넣어준다
- 이렇게 하면 어노테이션과 타입 기반으로 메서드 호출 시 필요한 인자들을 자동으로 채워줄 수 있다
6. 전체 코드
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String path = sc.nextLine();
Method[] methods = UserController.class.getDeclaredMethods();
UserController uc = new UserController();
for (Method method : methods) {
RequestMapping rm = method.getDeclaredAnnotation(RequestMapping.class);
if (rm == null) continue;
if (rm.value().equals(path)) {
Object[] argsToPass = new Object[method.getParameterCount()];
Annotation[][] paramAnnotations = method.getParameterAnnotations();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramAnnotations.length; i++) {
for (Annotation annotation : paramAnnotations[i]) {
if (annotation instanceof Princiapl) {
argsToPass[i] = SessionUser.getInstance();
}
}
if (argsToPass[i] == null) {
if (paramTypes[i] == Model.class) {
argsToPass[i] = Model.getInstance();
}
}
}
try {
method.invoke(uc, argsToPass);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
- 사용자 입력으로 받은
path
에 해당하는 메서드를UserController
에서 찾아 실행한다
- 메서드의 매개변수 개수만큼 배열을 만들고, 각 매개변수에 붙은 어노테이션과 타입 정보를 가져온다
- 각 파라미터가
@Princiapl
이 붙은 경우엔SessionUser
객체를 넣고, 없으면 타입이Model
인 경우Model
객체를 넣는다
- 준비된 인자 배열을 가지고
method.invoke
로 메서드를 실행한다
Share article