자바의 리플렉션은 클래스나 메서드의 메타정보를 이용해 메서드를 동적으로 호출하는 방법이다.
@Slf4j
static class Hello {
public String callA() {
log.info("callA");
return "A";
}
public String callB() {
log.info("callB");
return "B";
}
}
@Test
void reflection0 {
Hello target = new Hello();
log.info("start"); // 부가기능
String result1 = target.callA(); // 핵심기능
log.info("result = {}", result1); // 부가기능
log.info("start");
String result2 = target.callB();
log.info("result = {}", result2);
}
이 예시코드에서는 부가 기능 사이에 핵심 기능이 있고, 해당 핵심 기능은 계속해서 변화하기에 메서드로 따로 빼내기에 애매하다.
// 이렇게 쪼개면...
void logStart() { log.info("start"); }
void logResult(String result) { log.info("result = {}", result); }
// 쓸 때는 여전히 중복이 남고 지저분합니다.
logStart();
String result1 = target.callA();
logResult(result1);
logStart();
String result2 = target.callB();
logResult(result2);
예를들어 메서드로 따로 뺀다 하더라도 이렇게 중복이 남고 지저분한 코드가 완성된다.
따라서, 해당 문제를 해결하기 위해 리플렉션을 사용한다. 클래스와 메서드의 정보를 빼내어 동적으로 사용하는 방식이다.
@Test
void reflection1() throws ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
// 경로로 클래스의 메타정보를 가져와 리플렉션 객체를 생성한다.
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
// 실제 객체를 생성한다.
Hello target = new Hello();
// 리픒렉션 객체에서 매서드 정보를 가져온다.
Method methodCallA = classHello.getMethod("callA");
// 매서드가 호출될 객체를 파라미터로 넣는다.
// 호출의 결과를 Object로 반환한다.
Object result1 = methodCallA.invoke(target);
log.info("result = {}", result1);
Method methodCallB = classHello.getMethod("callB");
Object result2 = methodCallB.invoke(target);
log.info("result = {}", result2);
}
invoke() 메서드를 이용해 해당 메서드를 호출하게 되고, 결과를 Object 타입으로 변환시키는 것이다. 이때 주의해야할 점은 invoke() 메서드의 파리미터로는 실제 객체가 들어가야한다.
@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);
}
조금 더 간소화 하자면 이렇다.
하지만 리플렉션은 런타임시 동작하기 때문에 컴파일 시점에 오류를 알 수 없다는 단점이 존재한다. 따라서, 리플렉션의 사용을 최대한 지양한다.