Home Java - Reflection
Post
Cancel

Java - Reflection

Reflection

자바가 기본으로 제공하는 JDK 동적 프록시 기술이나 CGIB과 같은 프록시 생성 오픈소스 기술을 활용하면, 프록시 객체를 동적으로 만들어 낼 수 있다. 이러한 JDK 동적 프록시 기술이나 CGIB을 이해하기 위해선 리플렉션이라는 기술이 필수이다.

리프렉션은 클래스나 메소드의 메타정보를 동적으로 획득하고, 코드를 동적으로 호출할 수 있게 해주는 기술을 말한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package hello.proxy.jdkdynamic;

import java.lang.reflect.Method;

import org.junit.jupiter.api.Test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ReflectionTest {

  @Slf4j
  static class Hello {
    public String callA() {
      log.info("callA");
      return "A";
    }

    public String callB() {
      log.info("callB");
      return "B";
    }
  }

  @Test
  void reflection1() throws Exception {
    // 클래스 정보
    Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");

    Hello target = new Hello();

    Method methodCallA = classHello.getMethod("callA");
    Object result1 = methodCallA.invoke(target);
    log.info("result1={}", result1);

    Method methodCallB = classHello.getMethod("callB");
    Object result2 = methodCallB.invoke(target);
    log.info("result@={}", result2);
  }
}
  • Class.forName(…)
    • 클래스의 메타정보를 획득
    • 내부 클래스는 구분을 위해 $ 를 사용 함
  • classHello.getMethod(…)
    • 클래스의 메소드이름을 통해 해당 클래스의 메소드 메타정보를 획득
  • methodCallA.invoke(target)
    • 획득한 메소드 메타정보로 실제 인스턴스의 메소드를 호출
    • 위 예에서 methodCallA는 Hello 클래스의 callA() 라는 메소드 메타정보 이며, methodCallA.invoke(인스턴스)를 호출하면서 인스턴스를 넘겨주면 해당 인스턴스의 callA() 메소드를 찾아서 실행 함


target.callA()나 target.callB()와 같이 메소드를 직접 호출하지 않고, 위와 같은 방식으로 메소드 정보를 획득하여 메소드르 호출하는 주요 이슈는 클래스나 메소드 정보를 동적으로 변경할 수 있다는 점 때문이다.

아래와 같이 target.callA()나 target.callB() 처럼 메소드를 직접 호출하는 부분을 리플렉션을 통하여 메소드 호출 부분을 공통 로직으로 변경 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
void reflection2() throws Exception {
  Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");

  Hello target = new Hello();

  Method methodCallA = classHello.getMethod("callA");
  dynamicCall(methodCallA, target);;

  Method methodCallB = classHello.getMethod("callB");
  dynamicCall(methodCallB, target);;
}

private void dynamicCall(Method method, Object target) throws Exception {
  log.info("start");
  Object result = method.invoke(target);
  log.info("result@={}", result);
}

리플렉션 주의사항

리플렉션을 사용하면 클래스와 메소드의 메타정보를 사용하여 어플리케이션을 동적으로 유연하게 만들 수 있다. 그러나 리플레션 기술은 런타임에 동작하기 떄문에 컴파일 시점에 오류를 잡아낼 수 없다. 지금까지 프로그래밍 언어가 발전 하면서 타입정보를 기반으로 컴파일 시점에 오류를 잡는 좋은 오류탐지 방법을 사용하지 못하는 셈이다.

그러므로 리플렉션은 일반적으로 사용하면 안되고, 프레임워크 개발이나 또는 매우 일반적인 공통처리가 필요할때 부분적으로 주의해서 사용해야 한다.


참고

  • 스프링 핵심 원리 - 고급편(김영한)
This post is licensed under CC BY 4.0 by the author.