리플렉션(Reflection)은 자바에서 런타임에 클래스, 메서드, 필드 등의 정보를 동적으로 조사하고 조작할 수 있는 기능을 제공하는 API입니다. 즉, 컴파일 시점이 아닌 실행 시점에 객체의 구조를 분석하고, 그 구조에 따라 메서드를 호출하거나 필드 값을 변경하는 등의 작업을 수행할 수 있습니다.
💡 리플렉션의 핵심 개념
리플렉션을 사용하면 다음과 같은 작업을 할 수 있습니다:
- 클래스의 메타데이터(클래스 이름, 메서드, 필드 등)를 런타임에 동적으로 얻어낼 수 있습니다.
- 클래스의 필드 값을 읽거나 변경할 수 있습니다.
- 메서드를 호출하거나 생성자를 호출해 객체를 동적으로 생성할 수 있습니다.
- 애노테이션과 같은 메타데이터를 조사할 수 있습니다.
💡 리플렉션 API의 주요 기능
클래스 정보 가져오기:
Class<?>
객체를 통해 클래스 정보를 얻어낼 수 있습니다.Class.forName("클래스이름")
또는object.getClass()
로 클래스 객체를 가져옵니다.
Class<?> clazz = Class.forName("com.example.MyClass");
필드, 메서드, 생성자 정보 확인:
- 클래스에 선언된 필드, 메서드, 생성자의 정보를 런타임에 가져올 수 있습니다.
Field[] fields = clazz.getDeclaredFields(); // 클래스의 필드 목록 Method[] methods = clazz.getDeclaredMethods(); // 클래스의 메서드 목록 Constructor<?>[] constructors = clazz.getDeclaredConstructors(); // 클래스의 생성자 목록
필드 값 읽기 및 변경:
- 객체의 필드 값을 동적으로 읽거나 수정할 수 있습니다.
private
필드도 접근 제어를 우회하여 수정할 수 있습니다.
Field field = clazz.getDeclaredField("name"); field.setAccessible(true); // private 필드 접근 허용 String value = (String) field.get(instance); // 필드 값 가져오기 field.set(instance, "newName"); // 필드 값 설정
- 객체의 필드 값을 동적으로 읽거나 수정할 수 있습니다.
메서드 호출:
- 리플렉션을 사용해 특정 객체의 메서드를 호출할 수 있습니다.
Method method = clazz.getDeclaredMethod("sayHello", String.class); method.setAccessible(true); // private 메서드 접근 허용 method.invoke(instance, "World"); // 메서드 실행
생성자 호출 및 객체 생성:
- 리플렉션을 통해 클래스의 생성자를 호출하여 객체를 동적으로 생성할 수 있습니다.
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class); Object newInstance = constructor.newInstance("SomeName");
애노테이션 정보 가져오기:
- 클래스나 메서드에 선언된 애노테이션 정보를 런타임에 읽을 수 있습니다.
if (clazz.isAnnotationPresent(MyAnnotation.class)) { // 애노테이션이 있으면 처리 }
💡 리플렉션의 장점
- 유연성: 코드 작성 시점에 정의되지 않은 클래스나 메서드에 동적으로 접근할 수 있어, 유연한 구조를 설계할 수 있습니다.
- 프레임워크에서의 활용: 리플렉션은 Spring, Hibernate, JUnit 같은 프레임워크에서 핵심적인 역할을 합니다. 예를 들어, 스프링의 의존성 주입은 리플렉션을 통해 객체를 동적으로 생성하고 주입합니다.
- 동적 객체 생성: 런타임에 동적으로 객체를 생성하고 메서드를 호출할 수 있어, 컴파일 시점에 알 수 없는 클래스에 대한 작업도 가능합니다.
💡 리플렉션의 단점
- 성능 문제: 리플렉션은 일반적인 메서드 호출보다 성능이 떨어집니다. 런타임에 메타데이터를 분석하고 동적으로 메서드를 호출하는 과정에서 추가적인 비용이 발생합니다.
- 안정성 문제: 컴파일 시점에 타입이 검증되지 않기 때문에, 잘못된 메서드 호출이나 필드 접근으로 인해 런타임 오류가 발생할 가능성이 있습니다.
- 캡슐화 위반: 리플렉션은
private
메서드나 필드에도 접근할 수 있기 때문에, 객체지향 프로그래밍의 중요한 원칙인 캡슐화(encapsulation)를 위반할 수 있습니다.
💡 리플렉션의 실제 사용 예시
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
private void sayHello(String message) {
System.out.println(name + " says: " + message);
}
}
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 클래스 객체를 동적으로 가져오기
Class<?> clazz = Class.forName("Person");
// 생성자를 통해 객체 생성
Constructor<?> constructor = clazz.getConstructor(String.class);
Object person = constructor.newInstance("John");
// private 메서드 호출
Method method = clazz.getDeclaredMethod("sayHello", String.class);
method.setAccessible(true); // private 메서드 접근 허용
method.invoke(person, "Hello!"); // 메서드 호출
}
}
출력:
John says: Hello!
💡 리플렉션이 사용되는 곳
- Spring Framework: 의존성 주입, AOP(Aspect Oriented Programming) 등에 리플렉션이 사용됩니다.
- JUnit: 테스트 프레임워크에서 메서드나 클래스의 동적 실행에 리플렉션을 활용합니다.
- Hibernate: ORM에서 객체와 데이터베이스 테이블 간의 매핑을 처리하기 위해 리플렉션을 사용합니다.
결론
리플렉션은 런타임에 동적으로 클래스와 객체를 다룰 수 있는 강력한 도구로, 프레임워크나 라이브러리 개발에서 필수적입니다. 하지만 성능 저하와 캡슐화 위반 같은 단점도 있기 때문에, 필요할 때 적절히 사용하는 것이 중요합니다.
'Spring' 카테고리의 다른 글
Redirect(리다이렉트) (0) | 2024.10.10 |
---|---|
핸들러 메서드(Handler Method) - 컨트롤러 (0) | 2024.10.10 |
어노테이션 기반의 Spring 컨테이너 구성 (0) | 2024.09.04 |
Java Instrument API vs ASM(Abstract Syntax Manipulation) (0) | 2024.09.03 |
Mixin Pattern (자바에선 다중상속 허용X -> 믹스인디자인패턴으로 흉내) (0) | 2024.08.28 |