리플렉션은 실행 중인 자바 프로그램이 자체적으로 검사하고 자신의 구조와 동작을 수정할 수 있게 해주는 기능이다.
컴파일 타임:
- 클래스들의 메타데이터(클래스 정보, 메서드, 필드 등)를 수집
- 이 정보들이 클래스로더에 의해 로드됨
런타임:
- 프로그램 실행 중에 동적으로 클래스의 정보를 검사하고 조작 가능
- 클래스 이름이나 메서드 이름으로 해당 객체나 메서드를 찾아서 사용 가능
- 심지어 private 멤버에도 접근 가능
1. 클래스의 정보 접근:
Class<?> clazz = Class.forName("com.example.MyClass");
2. 필드 정보 접근 및 조작
Field[] fields = clazz.getDeclaredFields();
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // private 필드 접근 가능
3. 메서드 정보 접근 및 호출
Method method = clazz.getDeclaredMethod("methodName", parameterTypes);
method.setAccessible(true);
method.invoke(object, parameters);
스프링에서는 다음과 같이 활용된다.
1. 의존성 주입 (Dependency Injection)
@Autowired
private UserService userService; // 스프링이 리플렉션으로 주입
2. AOP(Aspect-Oriented Programming)
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
// 메서드 실행 전 로직
}
}
외에도 JSON 역/직렬화 등에 사용된다.
리플렉션은
- 일반 메서드 호출보다 느리다.
- private 멤버에 접근 가능하므로 캡슐화를 위반할 수 있다.
- 컴파일 시점 체크를 우회할 수 있어 타입 안정성이 감소될 수 있다.
꼭 필요한 경우가 아니면 일반적인 방식으로 코딩하는 것이 권장된다..
리플렉션 === 메타데이터를 알고 있는 것?
리플렉션은 단순히 "메타데이터를 알고 있는 것"이 아니라 "실행 중인 프로그램이 자기 자신의 구조(메타데이터)를 분석하고 수정할 수 있는 능력"을 말한다.
// 클래스의 모든 메서드를 찾아볼 수 있고
Method[] methods = MyClass.class.getDeclaredMethods();
// private 필드도 접근 가능하고
Field field = MyClass.class.getDeclaredField("privateField");
field.setAccessible(true);
// 새로운 인스턴스도 동적으로 만들 수 있다
Object instance = MyClass.class.newInstance();
public class Person {
private String name;
public void setName(String name) { this.name = name; }
private void secretMethod() { }
public String getName() { return name; }
}
// 메서드 정보 확인하기
Method[] methods = Person.class.getDeclaredMethods();
for (Method method : methods) {
System.out.println("메서드명: " + method.getName());
System.out.println("접근제어자: " + Modifier.toString(method.getModifiers()));
System.out.println("반환타입: " + method.getReturnType().getSimpleName());
System.out.println("------------");
}
=====================================================================================
------------
메서드명: setName
접근제어자: public
반환타입: void
------------
메서드명: secretMethod
접근제어자: private
반환타입: void
------------
메서드명: getName
접근제어자: public
반환타입: String
------------
그러니까 핵심은
프로그램이 실행 중에 자기 자신의 구조를 들여다볼 수 있다는 것
그 정보를 바탕으로 동적으로 뭔가를 할 수 있다는 것
이다.
Class<?> clazz = Person.class;
// 클래스의 모든 정보를 볼 수 있고
System.out.println(clazz.getModifiers()); // public, private 등
System.out.println(clazz.getSuperclass()); // 부모 클래스
System.out.println(clazz.getInterfaces()); // 구현한 인터페이스들
// Enum이라면 상수들도 볼 수 있고
if (clazz.isEnum()) {
Object[] enumConstants = clazz.getEnumConstants();
}
// 동적으로 객체도 만들 수 있고
Object instance = clazz.newInstance();
// 메서드를 찾아서 실행할 수도 있다
Method method = clazz.getDeclaredMethod("setName", String.class);
method.invoke(instance, "홍길동");
리플렉션은 자바만 가지고 있는 기능은 아니다.
리플렉션은 프로그래밍 언어들이 가지고 있는 일반적인 기능이다.
# Python 리플렉션
class MyClass:
def my_method(self):
pass
# 메서드 정보 가져오기
print(getattr(MyClass, 'my_method'))
// C# 리플렉션
Type type = typeof(MyClass);
MethodInfo method = type.GetMethod("MyMethod");
// Kotlin 리플렉션
class MyClass {
fun myMethod() {}
}
val method = MyClass::class.java.getDeclaredMethod("myMethod")
자바의 리플렉션이 특별히 더 많이 언급되는 이유는 스프링 같은 유명한 프레임워크들이 리플렉션을 많이 활용하기 때문이다..
아무튼
어렵게 생각할 게 아니라
그냥 런타임에 자기 자신의 클래스든 메서드든 접근할 수 있게 해주는 기술이다.
그렇게 접근할 수 있으니까 가져와서 객체를 만들든 인스턴스를 만들든 동적으로 뭘 할 수 있다.
// 1. 런타임에 클래스, 메서드, 필드 등에 접근할 수 있고
Class<?> clazz = MyClass.class;
Method method = clazz.getMethod("someMethod");
Field field = clazz.getDeclaredField("someField");
// 2. 접근한 것들로 이것저것 할 수 있다
// - 새로운 객체 만들기
Object instance = clazz.newInstance();
// - 메서드 실행하기
method.invoke(instance, parameters);
// - private 필드 값도 가져오거나 변경하기
field.setAccessible(true);
field.set(instance, newValue);
이게 전부다. 나머지는 이 기능을 활용해서 만든 것들이다.
clazz.newInstance()는 Java 9부터 deprecated되었기 때문에,
최신 버전에서는 Constructor를 통해 인스턴스를 생성하는 것이 권장됩니다.
------------------------------------------------------------------------
Object instance = clazz.newInstance(); // Deprecated된 방법
Constructor<?> constructor = clazz.getConstructor();
Object instance = constructor.newInstance(); // 권장 방법
------------------------------------------------------------------------
'Java' 카테고리의 다른 글
업캐스팅 다운캐스팅 (0) | 2025.01.07 |
---|---|
LSP (0) | 2024.12.29 |
리스트처리(for -> forEach), switch (enum), Null(Optional) (1) | 2024.12.25 |
추상화....인터페이스... (0) | 2024.12.24 |
클라이언트 ip 가져오기 (0) | 2024.12.24 |