Reflection

GoldenDusk·2023년 11월 20일
0

CS지식

목록 보기
16/26
post-thumbnail

Reflection

🍒 정의

자바에서 런타임시 클래스의 정보를 분석하고 조작하는 기법으로 컴파일 타임에는 알 수 없는 클래스를 동적으로 로딩하여 사용하거나, 클래스의 메소드, 필드, 생성자 등의 정보를 동적으로 분석할 수 있는 강력한 기능 제공

  • 자바에서 이미 로딩이 완료된 클래스에서 또는 다른 클래스를 동적으로 로딩하여 구체적인 타입을 알지 못하더라도 생성자, 멤버 필드, 그리고 멤버 메소드를 사용할 수 있는 기법이다.
  • 객체를 통해서 클래스의 패키지 정보, 접근 지정자, 부모 클래스, 어노테이션 등을 얻을 수 있다.
  • 즉, 핵심은 컴파일 타임이 아니라 런타임에 동적으로 컴파일 타임에는 알 수 없는 클래스를 특정 클래스의 정보를 객체화하여 분석 및 추출해낼 수 있는 동적 프로그래밍 기법이다.

참고

런타임(Runtime)은 프로그램이 실행되고 있는 동안의 시간을 가리킨다. 자바에서 런타임은 프로램이 메모리에 로드되어 실행되고 있는 상태를 의미하며, 여기서 런타임은 컴파일 시간과는 대조되며, 컴파일 시간소스 코드가 기계어 코드로 변환되는 시간을 의미한다.

자바 프로그램은 크게 두 단계로 나뉜다.

  1. 컴파일 타임(Compile Time): 자바 소스 코드가 자바 컴파일러에 의해 바이트 코드로 변환되는 단계이다. 이 바이트 코드는 JVM(Java Virtual Machine)에서 실행할 수 있는 중간 형태의 코드로 변환된다. 이 단계에서는 신택스 오류 등을 확인하고 기계어 코드로 변환된다.
  2. 런타임(Run Time): 변환된 바이트 코드가 JVM에서 실행되는 단계이다. 이 단계에서는 메모리에 프로그램이 로드되고, 객체가 생성되며, 메소드가 호출되는 등의 프로그램이 실제로 동작하는 시간을 의미한다. 런타임에는 사용자의 입력, 동적 메모리 할당, 예외 처리, 스레드 관리 등이 발생한다.

따라서 런타임이라는 용어는 프로그램이 실행되고 있는 동안의 시간 동안에 해당하는 모든 활동을 나타내며, 자바에서 런타임은 JVM이 자바 프로그램을 실행하는 동안의 상태와 활동을 가리킨다.

🍒 Reflection 사용 이유

특히 프레임워크, 라이브러리, 코드 생성 도구 등에서 활용되며, 예를 들어 의존성 주입(Dependency Injection), 객체 직렬화(Serialization), 테스트 프레임워크 등에서 Reflection이 사용

  1. 동적 클래스 로딩: 프로그램이 실행 중에 동적으로 클래스를 로딩할 수 있다. 이는 특정 클래스의 이름을 문자열로 받아 해당 클래스를 동적으로 로딩하여 인스턴스를 생성할 수 있다.
  2. 런타임에서의 동적 분석: Reflection을 사용하면 객체의 클래스, 필드, 메소드 등의 정보를 동적으로 분석할 수 있다. 이는 유연하게 객체를 다루고 동적으로 상호작용할 수 있도록 한다.
  3. 프레임워크와 라이브러리 개발: 많은 프레임워크와 라이브러리는 Reflection을 사용하여 외부에서 제공된 클래스를 동적으로 조작하여 일반적인 동작을 수행하게 된다. 예를 들어, 자동화된 매핑, 직렬화, 그리고 테스트 프레임워크 등이 Reflection을 사용한다.

사용 예시

  1. 의존성 주입(Dependency Injection)
  • 프레임워크나 컨테이너에서 객체를 생성하고 관리할 때, Reflection을 사용하여 클래스의 생성자에 필요한 의존성을 동적으로 주입할 수 있다.
Class<?> clazz = Class.forName("com.example.MyClass");
Constructor<?> constructor = clazz.getConstructor(Dependency.class);
Object instance = constructor.newInstance(new Dependency());
  1. JUnit 테스트 프레임워크:
  • JUnit은 테스트 케이스 클래스에서 특정 어노테이션이 붙은 메소드를 찾아 실행하는데, Reflection을 사용하여 테스트 메소드를 동적으로 찾고 실행한다.
Class<?> testClass = MyTestCase.class;
for (Method method : testClass.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Test.class)) {
        method.invoke(testClass.newInstance());
    }
}
  1. 자바 리플렉션 API 사용
  • Reflection API를 사용하여 클래스의 정보를 동적으로 가져오고 활용할 수 있다.
Class<?> myClass = MyClass.class;

// 클래스의 모든 메소드 가져오기
Method[] methods = myClass.getDeclaredMethods();

// 클래스의 필드 가져오기
Field[] fields = myClass.getDeclaredFields();
  1. 스프링 프레임워크의 빈 등록
Class<?> beanClass = MyBean.class;
Object beanInstance = beanClass.newInstance();
// 빈 등록
applicationContext.registerBean(MyBean.class, () -> beanInstance);
  1. 객체 직렬화(Serialization):
  • 객체를 직렬화하고 역직렬화할 때 Reflection을 사용하여 클래스의 구조를 동적으로 분석한다.
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.ser"));
oos.writeObject(new MySerializableObject());

🍒 Reflection 사용 주의점

  1. 성능 문제: Reflection은 정적 코드에 비해 런타임 성능이 떨어질 수 있다. Reflection 작업은 컴파일러 최적화를 할 수 없고, 타입 안정성을 보장할 수 없기 때문이다. 성능이 중요한 부분에서 빈번한 Reflection 사용은 피하는 것이 좋다.
  2. 보안 문제: Reflection을 통해 private 멤버에 접근이 가능하므로, 보안에 민감한 부분에서는 조심해야 한다. setAccessible(true) 메소드를 통해 접근 권한을 무시할 수 있기 때문에 신뢰할 수 없는 데이터로부터 Reflection을 사용하는 경우 주의가 필요하다.
  3. 타입 안정성 유지: Reflection을 사용할 때는 컴파일러가 제공하는 타입 안정성을 보장하기 어렵다. 잘못된 타입의 클래스를 로딩하거나 잘못된 메소드를 호출할 수 있으므로 주의가 필요하다.

Reflection은 특별한 상황에서 강력한 기능을 제공하지만, 성능 및 타입 안정성 등의 측면에서 조심해서 사용해야 한다. 코드의 가독성과 유지보수성을 위해서는 Reflection 사용을 최소화하고, 필요한 경우에만 사용하는 것이 좋다.

profile
내 지식을 기록하여, 다른 사람들과 공유하여 함께 발전하는 사람이 되고 싶다. gitbook에도 정리중 ~

0개의 댓글