개발하는 자몽

리플렉션(Reflection) 본문

Java

리플렉션(Reflection)

jaamong 2023. 3. 28. 14:22
 

스프링 핵심 원리 - 고급편 - 인프런 | 강의

스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., - 강의 소개 | 인프런

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라는 메타정보로 추상화했다. 덕분에 공통 로직을 만들 수 있게 되었다.

 

 

 

리플렉션을 이용하여 동적으로 코드를 호출하는 등 공통 로직을 만들 수 있게 되었지만, 런타임 시 이루어지기 때문에 컴파일 때 오류가 잡히지 않는다. 이 점을 유의하고 정말 필요한 때에 사용해야 한다.

Comments