
리플렉션 (Reflection)은 자바에서 클래스나 멤버에 대한 정보를 런타임에 조사하고, 조작할 수 있는 기능이다. 예를 들어, 클래스의 이름, 메서드, 필드, 생성자, 어노테이션 등에 대한 정보를 프로그램 실행 중에 알아내고, 이를 통해 객체를 생성하거나 메서드를 호출할 수 있다. 이 기능 덕분에, 개발자는 코드의 유연성과 확장성을 높일 수 있다.
이러한 기능을 가진 리플렉션은 프레임워크 (예: 스프링 등)에서 많이 사용되는데 예를 들어, 의존성 주입 (Dependency Injection)이 그 중 하나다. 스프링은 리플렉션을 통해서 클래스의 메타데이터를 분석하고, @Autowired 어노테이션이 붙은 필드를 찾아서 자동으로 의존성을 주입해 준다.
이 과정에서 스프링 컨테이너는 리플렉션을 사용해서 해당 필드의 타입에 맞는 빈 (bean)을 찾고, 그 빈을 해당 필드에 할당한다. 이를 통해 개발자는 객체의 생성과 의존성 관리를 수동으로 하지 않아도 된다. 이런 방식으로 스프링은 개발자가 더 쉽고 유연하게 애플리케이션을 구성하고 확장할 수 있게 도와준다.
자바 리플렉션의 특징은 다음과 같다.
| 특징 | 설명 |
|---|---|
| 유연성 | 실행 시에 동적으로 클래스를 다룰 수 있기 때문에, 정적 코딩으로는 불가능한 작업을 수행 |
| 프로그래밍 복잡도 | 동적으로 코드를 작성하기 때문에 일반 코드보다 복잡함 |
| 성능 오버헤드 | 리플렉션을 사용할 경우 일반적인 메서드 호출보다 느릴 수 있음. 따라서 성능에 민감한 부분에서는 주의 필요 |
| 보안 문제 | private 멤버에 접근할 수 있으므로, 잘못 사용하면 보안 취약점이 발생 가능 |
| 유지보수 | 코드의 의존성이 명시적이지 않아, 리팩토링 시 주의가 필요 |
아래 예제는 Person 클래스를 대상으로 리플렉션을 사용해 private 필드와 메서드에 접근하는 방법을 보여준다.
✍️ 작성
package com.example;
public class Person {
private String name = "홍길동";
public Person() {}
private void printName() {
System.out.println("이름: " + name);
}
}
✍️ 작성
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionExample {
public static void main(String[] args) {
try {
// 1. 문자열로부터 Person 클래스의 Class 객체를 얻어온다.
Class<?> personClass = Class.forName("com.example.Person");
// 2. 기본 생성자를 사용하여 인스턴스 생성 (동적 객체 생성)
Object personInstance = personClass.getDeclaredConstructor().newInstance();
// 3. private 필드 'name'에 접근하기 위해 Field 객체를 얻어온다.
Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true); // private 필드 접근 허용
String nameValue = (String) nameField.get(personInstance);
System.out.println("private 필드 값: " + nameValue);
// 4. private 메서드 'printName'에 접근하여 호출한다.
Method printMethod = personClass.getDeclaredMethod("printName");
printMethod.setAccessible(true); // private 메서드 접근 허용
printMethod.invoke(personInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1. Class.forName("com.example.Person")
"com.example.Person"을 기반으로 해당 클래스(Person)를 로딩해서 Class 객체를 반환2. 동적 객체 생성
getDeclaredConstructor().newInstance()를 통해 Person 클래스의 기본 생성자를 호출하여 인스턴스를 생성.Object이므로, 특별한 처리가 없다면 형변환 없이 Object로 사용하거나 필요에 따라 캐스팅 가능.3. private 멤버 접근
getDeclaredField("name")과 getDeclaredMethod("printName")를 사용해 Person 클래스의 private 필드와 메서드에 접근.setAccessible(true)로 접근 제한을 해제한 후 필드 값을 읽거나 메서드를 호출.본 예제에서는 그냥 Class.forName("com.example.Person")으로 어떤 클래스를 받을지 명시해놓았지만 실제 애플리케이션에서는 이 부분이 외부 설정 파일이나 사용자 입력 등으로부터 받아온 문자열에 의해 결정될 수 있다.
즉, 코드 작성 시점에 어떤 클래스가 로딩될지 미리 정해져 있는 게 아니라, 실행 중에 입력된 값에 따라 동적으로 클래스를 로딩할 수 있다는 의미이다.
[Java] 리플렉션 (Reflection)이란 무엇일까? (개념/ 예시)
[Java] 자바 리플렉션(reflection)이란?