![[자바] 자바 리플렉션](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Fapi%2Fog-custom%3Ftitle%3D%255B%25EC%259E%2590%25EB%25B0%2594%255D%2B%25EC%259E%2590%25EB%25B0%2594%2B%25EB%25A6%25AC%25ED%2594%258C%25EB%25A0%2589%25EC%2585%2598%26tag%3DTemplate%2B1%26description%3D%26template%3D3%26backgroundImage%3Dhttps%253A%252F%252Fsource.inblog.dev%252Fog_image%252Fdefault.png%26bgStartColor%3D%2523723c3c%26bgEndColor%3D%2523723c3c%26textColor%3D%2523000000%26tagColor%3D%2523000000%26descriptionColor%3D%2523000000%26logoUrl%3D%26blogTitle%3DGyeongwon%2527s%2Bblog&w=2048&q=75)
1. 리플렉션(Reflection)
실행 중(Class가 메모리에 올라간 후)에 클래스, 메서드, 필드, 생성자 등의 구조를 조회하고 조작할 수 있는 기능
코드 실행 중에 클래스 정보를 읽어내고 조작 가능하다(런타임 분석), A 개발자의 App.java를 건드리지 않고 B 개발자가
UserController.java
를 자유롭게 수정 가능하다(유연성 확보),Spring, JUnit, Jackson 등은 모두 리플렉션을 사용한다.
리플렉션의 문제점
모든 메서드/필드를 다 뒤지는 것은 비효율적이다. 그렇게 하면 실행 속도가 느려지고 복잡도가 상승한다.
어노테이션(Annotation) + 리플렉션
“깃발 꽂기 → 깃발만 보기”
즉, 의미 있는 곳에만 어노테이션을 붙이고, 리플렉션은 어노테이션이 붙은 대상만 처리하면 된다.
2. 어노테이션(Annotation)
“이 코드에 의미 있는 정보를 붙이기”
자바 컴파일러, 개발자, 또는 프레임워크(Spring, JUnit 등)에게 이 부분은 특별한 처리를 해줘라고 표시하는 깃발이자 약속이다.
어디에 쓸까 ?
- 컴파일러용 지시어
@Override
,@SuppressWarnings
- 런타임 처리용 깃발
@Controller
,@Autowired
,@RequestMapping
- 소스 코드 문서화
@Deprecated
,@FunctionalInterface
3. 자바 리플렉션 실습
런타임 처리용 깃발

실습
App.java 는 A 개발자가 만들고, UserController.java 는 B 개발자가 만든다고 가정한다. B개발자가 A개발자의 코드를 건드리지 않고 본인 코드를 수정하기
커스텀 어노테이션 정의
package java_reflection;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 커스텀 어노테이션 정의
@Retention(RetentionPolicy.RUNTIME) // 런타임에도 어노테이션 정보를 유지해서 리플렉션으로 접근 가능하게 함.
@Target(ElementType.METHOD) // 이 어노테이션은 메서드에만 붙일 수 있음.
public @interface RequestMapping {
String value(); // 어노테이션에 @RequestMapping("/login") 이런 식으로 사용할 수 있도록 함.
}
A 개발자가 개발함, 리플렉션 기반 실행 -> App 클래스에서 실행 시 사용자 입력을 받아서 해당 메서드를 동적으로 호출
package java_reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Scanner;
// A 개발자가 개발함, 리플렉션 기반 실행 -> App 클래스에서 실행 시 사용자 입력을 받아서 해당 메서드를 동적으로 호출
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String path = sc.nextLine();
/* UserController.class
→ UserController 클래스의 Class 객체를 가져오는 리플렉션 방식이야.
클래스는 실행 중에 메타정보를 담고 있는 Class 객체로 표현됨.
.getDeclaredMethods()
→ 이 클래스에 선언된 모든 메서드를 가져와서 배열로 리턴해줌.
private, public, protected 전부 다 포함
상속받은 건 제외됨 (자기 클래스 안에 선언된 것만) */
Method[] methods = UserController.class.getDeclaredMethods();
// .invoke()로 메서드를 호출하기 위해서 UserController 객체를 직접 생성
UserController us = new UserController();
for (Method method : methods) {
// 현재 method에 붙은 @RequestMapping 어노테이션이 있는지 확인하고 가져옴, 없으면 null, 있으면 어노테이션 객체가 리턴됨.
Annotation anno = method.getAnnotation(RequestMapping.class);
// anno는 타입이 Annotation이라서 다운캐스팅을 해줌.
RequestMapping rm = (RequestMapping) anno;
// value() 값이 일치하면 그 메서드를 실행함.
if (rm.value().equals(path)) {
try {
// method.invoke(obj, args...)는 리플렉션을 통해 런타임에 메서드를 직접 실행시키는 방법이다.
// 마치 obj.메서드명()을 코드가 아니라 데이터처럼 다루는 느낌!
// 핵심임. 리플렉션을 통해 메서드를 동적으로 호출
method.invoke(us);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
}
B 개발자가 개발함, 메서드마다 어노테이션을 붙임
package java_reflection;
// B 개발자가 개발함, 메서드마다 어노테이션을 붙임
public class UserController {
@RequestMapping("/login") // 경로를 매핑하는 어노테이션을 붙임
public void login() {
System.out.println("login 호출됨");
}
@RequestMapping("/logout")
public void logout() {
System.out.println("logout 호출됨");
}
@RequestMapping("/join")
public void join() {
System.out.println("join 호출됨");
}
@RequestMapping("/delete")
public void delete() {
System.out.println("delete 호출됨");
}
}
Java는 컴파일 시 default 파라미터 이름 정보를 버린다. 따라서
.getParameters()
로 이름을 보기 위해선 컴파일 옵션이 필요하다.IntelliJ나 CLI에서 컴파일할 때
parameters
옵션을 주면 된다. 그래야 .getParameters()[0].getName()
이 진짜 username
, password
같이 나온다.javac -parameters App.java
Share article