Java

리플렉션

99duuk 2025. 1. 12. 15:04

리플렉션은 실행 중인 자바 프로그램이 자체적으로 검사하고 자신의 구조와 동작을 수정할 수 있게 해주는 기능이다. 

 

컴파일 타임:

  • 클래스들의 메타데이터(클래스 정보, 메서드, 필드 등)를 수집
  • 이 정보들이 클래스로더에 의해 로드됨

런타임:

  • 프로그램 실행 중에 동적으로 클래스의 정보를 검사하고 조작 가능
  • 클래스 이름이나 메서드 이름으로 해당 객체나 메서드를 찾아서 사용 가능
  • 심지어 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