본문 바로가기
JAVA

[자바] 리플렉션

by 감자b 2024. 12. 25.

리플렉션이란 자바에서 클래스가 제공하는 다양한 정보를 동적으로 분석하고 사용하는 기능을 의미한다. 해당 기능을 통해 런타임 시점에 클래스에 메타데이터 정보를 얻거나, 동적으로 새로운 객체를 생성, 메서드 호출, 필드 값을 을 읽고 쓸 수 있다.

클래스 메타데이터 조회 3가지

  1. 클래스 타입으로 조회
Class<Target> targetClass = Target.class;
  1. 인스턴스에서 조회
Target target = new Target();
Class<? extends Target> targetClass = target.getClass();
  1. 문자(패키지+클래스명)를 통한 조회
String targetPackage = "reflection.ReflectionTest";
Class<?> targetClass = Class.forName(targetPackage);

3번의 경우를 보면 문자열을 통해 클래스의 메타데이터를 조회할 수 있다.

→ 문자열을 동적으로 입력받아 메타데이터 조회가 가능!

 

 

Class 클래스의 주요 메서드

  • Class 클래스는 객체의 필드, 수정자, 생성자, 메서드 등을 getXxx 메서드를 통해 얻을 수 있다.
  • 대표적으로 get 메서드에는 두 가지가 존재한다
    • getXxx (getFields(), getAnnotations(), getMethods(), getModifiers() …)
      • 해당 클래스와 상위 클래스에서 상속된 모든 public Xxx를 반환
    • getDeclaredXxx (getDeclaredMethods(), getDeclaredConstructors()…)
      • 해당 클래스에 선언된 모든 Xxx를 접근 제어자에 관계없이 반환, 상속된 메서드는 포함 X

아래는 해당 클래스에 선언된 메서드들을 이름으로 찾은 뒤 호출하는 코드이다.

Target 클래스에는 public void call(String message), private void call2(String message) 두 메서드가 있다.

첫 번째 call의 경우에는 문제없이 호출된다.

두 번째 call2()의 경우에는 private 접근 제어자가 선언되어 있어 호출할 수 없다고 나온다.

마지막 call3()는 없는 메서드이므로 예외가 발생한다.

일반적으로 메서드 호출을 정적으로 이루어졌지만 리플렉션을 사용하면 동적으로 메서드 이름을 받은 뒤 호출하는 것이 가능해진다.

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Target target = new Target();
        String[] methodNames = {"call", "call2", "call3"};
        call(target, methodNames);
    }

    public static void call(Target target, String[] methodNames) {
        Class<? extends Target> targetClassInfo = target.getClass();
        for (String methodName : methodNames) {
            try {
                Method method = targetClassInfo.getDeclaredMethod(methodName, String.class);
                method.invoke(target, "hello");
            } catch (Exception e) {
                System.out.println(methodName + " 메서드를 호출할 수 없습니다: " + e.getMessage());
            }
        }
    }
}

Target.call : hello

call2 메서드를 호출할 수 없습니다:

class reflection.ReflectionTest cannot access a member of class reflection.Target with modifiers "private"

call3 메서드를 호출할 수 없습니다: reflection.Target.call3(java.lang.String)

 

리플렉션을 사용하면 또 하나 특별한 기능을 제공한다.

바로 call2()와 같이 private 접근 제어자가 붙은 경우에도 호출이 가능해진다.

기본적으로는 위와 같이 호출할 수 없도록 되어있지만 접근하려는 필드, 메서드에 setAccessible(true)

설정을 하면 접근이 가능하고 값 변경도 가능하다.

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Target target = new Target();
        String[] methodNames = {"call", "call2", "call3"};
        call(target, methodNames);
    }

    public static void call(Target target, String[] methodNames) {
        Class<? extends Target> targetClassInfo = target.getClass();
        for (String methodName : methodNames) {
            try {
                Method method = targetClassInfo.getDeclaredMethod(methodName, String.class);
                method.setAccessible(true);
                method.invoke(target, "hello");
            } catch (Exception e) {
                System.out.println(methodName + " 메서드를 호출할 수 없습니다: " + e.getMessage());
            }
        }
    }
}

 

Target.call : hello

Target.call2 : hello

call3 메서드를 호출할 수 없습니다: reflection.Target.call3(java.lang.String)


장단점

장점

  • 런타임 시점에 객체 조작이 가능하므로 확장에 유리하고, 유연하다는 장점이 있다.
  • 스프링은 리플렉션을 통해 런타임 시점에 메타데이터를 분석해서, 필요한 객체 생성, 주입을 한다.
  • 테스트 코드 작성 시 대상 클래스의 private 메서드, 필드에 접근하여 정밀한 테스트가 가능하다.
    • 주로 테스트, 프레임워크나 라이브러리 개발에 유용되게 사용된다

단점

  • 위에서 본 것처럼 리플렉션을 활용하면 private 접근 제어자에 접근, 값 변경이 가능하다.
  • 하지만 private 접근 제어자는 클래스 내부 데이터를 보호하고, 외부에서의 직접적인 접근을 방지하기 위해 사용되는데 이를 위반한다. → 캡슐화, 정보은닉 X
  • 그리고 클래스의 구조, 구현 사항이 변경되는 경우 리플렉션을 사용한 코드는 예상치 못한 버그가 초래된다.
  • 런타임에 클래스를 분석하므로 정적 메서드 호출 방법보다 느리다.
  • 일반적인 애플리케이션 코드에서는 권장 X

'JAVA' 카테고리의 다른 글

[자바] URL 인코딩  (1) 2024.12.25
[자바] 애노테이션(Annotation)  (1) 2024.12.25
[자바] 소켓 프로그래밍  (0) 2024.12.25
[자바] I/O 스트림  (0) 2024.12.25
[자바] 스레드 풀, Executor  (0) 2024.12.25