본문 바로가기
카테고리 없음

jdk 동적 프록시, 리플렉션(reflection), CGLIB

by 문자메일 2022. 10. 10.

JDK 동적 프록시와 CGLIB(Code Generate Library) 차이

- CGLIB는 바이트코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리

- 인터페이스가 있어야만 사용할 수 있는 JDK 동적 프록시와 달리 CGLIB를 사용하면 구체 클래스만 가지고도 동적 프록시를 만들어낼 수 있다.

- CGLIB는 외부 라이브러리인데, 스프링 프레임워크가 스프링 내부 소스 코드에 포함하여 별도의 외부 라이브러리를 추가하지 않아도 사용할 수 있다.

 

리플렉션(reflection)

JDK 동적 프록시를 이해하기 위해서는 먼저 자바의 리플렉션 기술을 이해해야 한다.

리플렉션 기술을 사용하면 클래스나 메서드의 메타정보를 동적으로 획득하고, 코드를 동적으로 호출할 수 있다.

 

일반적인 경우, 대부분의 로직은 공통이지만 중간에 인스턴스에서 호출하는 메서드가 다를 경우 공통화 하기 어렵다.

(ex : target.callA(), target.callB();)

이럴때 클레스나 메서드의 메타정보를 사용할 수 있는 리플렉션 기술을 사용하여, 컴파일 시점이 아닌 런타임시점에 호출하는 메서드를 동적으로 바꿀 수 있다.

아래 코드의 dynamicCall() 메서드 부분이 리플렉션 기술을 이용하여 공통화 한 결과.

 

주의사항 : 

접근 지시자를 무시할 수 있는 문제가 발생할 수 있는 가능성이 존재한다.

또한 리플렉션을 사용하면 클래스와 메서드의 메타정보를 사용해서 애플리케이션을 동적으로 유연하게 만들 수 있다.

하지만 리플렉션 기술은 런타임에 동작하기 때문에 컴파일 시점에 오류를 잡을 수 없는 문제가 있다.

getMethod("callXXXX") 처럼 존재하지 않는 메서드의 이름을 실수로 넣었을 때, 컴파일 시점에 에러가 발생하지 않고 따라서 런타임 시점에서야 오류를 발견할 수 있는 문제가 있다.

그래서 컴파일 시점에 오류를 잡을 수 있도록 가급적이면 리플렉션 기술은 사용하면 안된다.

 

리플랙션으로 읽어오는 방법 3가지 예시

 

 

@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 종료
    }

    @Test
    void reflection1() 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 result1 = method.invoke(target);
        log.info("result={}", result1);
    }

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

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

}

 

 

 

동적 프록시 

 

 

 

 

댓글