리플렉션(Reflection)
스프링 핵심 원리 - 고급편 - 인프런 | 강의
스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
섹션 5. 동적 프록시 기술 - 리플렉션
리플렉션에 대해 정리해보자.
JDK 동적 프록시를 이해하기 위해서는 리플렉션(Reflection)을 알아야 한다. 이 기술은 JDK 동적 프록시와 마찬가지로 자바에서 제공한다.
리플렉션을 이용하면 클래스나 메서드의 메타정보를 동적으로 획득하고, 코드도 동적으로 호출할 수 있다.
@Slf4j
public class ReflectionTest {
@Test
void reflection0() {
Hello target = new Hello();
//공통 로직1 시작
log.info("start");
String result1 = target.callA(); //호출하는 메서드가 다름, 동적 처리 필요
log.info("result={}", result1);
//공통 로직1 종료
// 공통 로직2 시작
log.info("start");
String result2 = target.callB(); //호출하는 메서드가 다름, 동적 처리 필요
log.info("result={}", result2);
//공통 로직2 종료
}
...
}
Hello라는 클래스가 있고 해당 클래스에는 callA(), callB()라는 메서드가 있다고 하자. 위 코드는 두 메서드를 호출하는 부분을 제외하면 동일한 로직을 가지고 있다. 이러한 동일한 로직을 리플렉션을 통해 동적으로 호출할 수 있다.
@Slf4j
public class ReflectionTest {
...
@Test
void reflection1() throws Exception {
//클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA 메서드 정보
Method methodCallA = classHello.getMethod("callA");
Object result1 = methodCallA.invoke(target); //target 인스턴스에 있는 callA()를 호출한다.
log.info("result1={}", result1);
//callB 메서드 정보
Method methodCallB = classHello.getMethod("callB");
Object result2 = methodCallB.invoke(target); //target 인스턴스에 있는 callB()를 호출한다.
log.info("result2={}", result2);
}
...
}
Class.forName()을 이용하여 호출할 메서드가 속한 클래스의 정보를 가져온다. forName()에 클래스 이름을 넘길 때, 내부 클래스는 $를 이용하여 표시한다.
호출할 메서드의 정보는 위에서 획득한 클래스의 정보를 이용하여 가져온다 : getMethod("호출할 메서드 명"). 이렇게 가져온 메서드 정보는 Method라는 클래스로 추상화된다.
마지막으로 클래스 인스턴스(target, forName()의 대상)에 있는 메서드를 호출한다. 호출한 결과는 Object로 반환되는데, 이는 최상위 클래스이므로 어떤 인스턴스라도 받을 수 있다.
위 코드에서 호출할 메서드의 정보는 Method라는 클래스로 추상화되고, Object가 최상위 클래스라는 점을 떠올려보자. 해당 사실을 통해 위 코드를 아래처럼 작성할 수 있다.
@Slf4j
public class ReflectionTest {
...
@Test
void reflection2() throws Exception {
//클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA 메서드 정보
Method methodCallA = classHello.getMethod("callA");
dynamicCall(methodCallA, target);
//callB 메서드 정보
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);
}
...
}
결과적으로 정적인 target.callA() , target.callB() 코드를 리플렉션을 사용해서 Method라는 메타정보로 추상화했다. 덕분에 공통 로직을 만들 수 있게 되었다.
리플렉션을 이용하여 동적으로 코드를 호출하는 등 공통 로직을 만들 수 있게 되었지만, 런타임 시 이루어지기 때문에 컴파일 때 오류가 잡히지 않는다. 이 점을 유의하고 정말 필요한 때에 사용해야 한다.