You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
지금까지 프록시를 적용하기 위해 적용 대상의 숫자만큼 많은 프록시 클래스를 만들었다. 적용 대상이 100개면 프록시 클래스도 100개를 만들었다. 그런데 앞서 살펴본 것과 같이 프록시 클래스의 기본 코드와 흐름은 거의 같고 프록시를 어떤 대상에 적용하는가 정도만 차이가 있었다. 쉽게 이야기해서 프록시의 로직은 같은데 적용대상만 차이가 있는 것이다.
이 문제를 해결하는 것이 바로 동적 프록시 기술이다.
동적 프록시 기술을 사용하면 개발자가 직접 프록시 클래스를 만들지 않아도 된다. 이름 그래도 프록시 객체를 동적으로 런타임에 개발자가 대신 만들어준다. 그리고 동적 프록시에 원하는 실행 로직을 저장할 수 있다.
사실 동적 프록시는 말로는 이해하기 쉽지 않다. 바로 예제코드를 보자
주의
JDK 동적 프록시는 인터페이스 기반으로 프록시를 동적으로 만들어준다. 따라서 인터페이스가 필수이다.
먼저 자바 언어가 기본으로 제공하는 JDK 동적 프록시를 알아보자.
기본 예제 코드
JDK 동적 프록시를 이해하기 위해 아주 단순한 예제코드를 만들어보자.
간단히 ‘A’, ‘B’ 클래스를 만드는데 JDK 동적 프록시는 인터페이스가 필수이다. 따라서 인터페이스와 구현체로 구분했다.
JDK 동적 프록시는 InvocationHandler.invoke() 를 호출한다. TimeInvocationHandler 가 구현체로 있으므로 TimeInvocationHandler.invoke() 가 호출된다.
TimeInvocationHandler가 내부 로직을 수행하고, method.invoke(target, args) 를 호출해서 target 인 실제 객체(AImpl)를 호출한다.
AImpl 인스턴스의 call() 이 실행된다.
AImpl 인스턴스의 call() 의 실행이 끝나면 TimeInvocationHandler 로 응답이 돌아온다. 시간 로그를 출력하고 결과를 반환한다.
실행 순서 그림
동적 프록시 클래스 정보
dynamicA()와 dynamicB() 둘을 동시에 함께 실행하면 JDK 동적 프록시가 각각 다른 동적 프록시 클래스를 만들어주는 것을 확인할 수 있다.
정리
예제를 보면 AImpl, BImpl 각각 프록시를 만들지 않았다. 프록시는 JDK 동적 프록시를 사용해서 동적으로 만들고 TimeInvocationHandler 는 공통으로 사용했다.
JDK 동적 프록시 기술 덕분에 적용 대상 만큼 프록시 객체를 만들지 않아도 된다. 그리고 같은 부가 기능 로직을 한번만 개발해서 공통으로 적용할 수 잇다. 만약 적용 대상이 100개여도 동적 프록시를 통해서 생성하고 각각 필요한 InvocationHandler 만 만들어서 넣어주면 된다.
결과적으로 프록시 클래스를 수없이 만들어야 하는 문제도 해결하고 부가 기능 로직도 하나의 클래스에 모아서 단일 책임 원칙(SRP)도 지킬 수 있게 되었다.
JDK 동적 프록시 없이 직접 프록시를 만들어서 사용할 때와 JDK 동적 프록시를 사용할 때의 차이를 그림으로 비교해보자.
LogTrace 에 사용할 메세지이다. 프록시를 직접 개발할때는 OrderController.request() 와 같이 프록시마다 호출되는 클래스의 메서드 이름을 직접 남겼다. 이제는 Method 를 통해서 호출되는 메서드 정보와 클래스 정보를 동적으로 확인할 수 있기 때문에 이 정보를 사용하면 된다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
JDK 동적 프록시 - 소개
지금까지 프록시를 적용하기 위해 적용 대상의 숫자만큼 많은 프록시 클래스를 만들었다. 적용 대상이 100개면 프록시 클래스도 100개를 만들었다. 그런데 앞서 살펴본 것과 같이 프록시 클래스의 기본 코드와 흐름은 거의 같고 프록시를 어떤 대상에 적용하는가 정도만 차이가 있었다. 쉽게 이야기해서 프록시의 로직은 같은데 적용대상만 차이가 있는 것이다.
이 문제를 해결하는 것이 바로 동적 프록시 기술이다.
동적 프록시 기술을 사용하면 개발자가 직접 프록시 클래스를 만들지 않아도 된다. 이름 그래도 프록시 객체를 동적으로 런타임에 개발자가 대신 만들어준다. 그리고 동적 프록시에 원하는 실행 로직을 저장할 수 있다.
사실 동적 프록시는 말로는 이해하기 쉽지 않다. 바로 예제코드를 보자
주의
JDK 동적 프록시는 인터페이스 기반으로 프록시를 동적으로 만들어준다. 따라서 인터페이스가 필수이다.
먼저 자바 언어가 기본으로 제공하는 JDK 동적 프록시를 알아보자.
기본 예제 코드
JDK 동적 프록시를 이해하기 위해 아주 단순한 예제코드를 만들어보자.
간단히 ‘A’, ‘B’ 클래스를 만드는데 JDK 동적 프록시는 인터페이스가 필수이다. 따라서 인터페이스와 구현체로 구분했다.
AInterface
AImpl
BInterface
BImpl
JDK 동적 프록시 - 예제 코드
JDK 동적 프록시 InvocationHandler
JDK 동적 프록시에 적용할 로직은
InvocationHandler인터페이스를 구현해서 작성하면 된다.JDK 동적 프록시가 제공하는 InvocationHandler
제공되는 파라미터 3가지
구현코드
TimeInvocationHandler
TimeInvocationHandler는InvocationHandler인터페이스를 구현한다. 이렇게 해서 JDK 동적 프록시에 적용할 공통 로직을 개발할 수 있다.Object target: 동적 프록시가 호출한 대상method.invoke(target, args): 리플렉션을 사용해서target인스턴스의 메서드를 실행한다args는 메서드 호출시 넘겨줄 인수이다.이제 테스트 코드로 JDK 동적 프록시를 사용해보자.
JdkDynamicProxyTest
java.lang.reflect.Proxy를 통해서 생성할 수 있다.실행
로직 이해하기
TimeInvocationHandler 클래스의 invoke 로 가서 result
Object result = method.invoke(target, args); //calldynamicB 메서드
실행
실행순서
call()을 실행한다.InvocationHandler.invoke()를 호출한다.TimeInvocationHandler가 구현체로 있으므로TimeInvocationHandler.invoke()가 호출된다.TimeInvocationHandler가 내부 로직을 수행하고,method.invoke(target, args)를 호출해서target인 실제 객체(AImpl)를 호출한다.AImpl인스턴스의call()이 실행된다.AImpl인스턴스의call()의 실행이 끝나면TimeInvocationHandler로 응답이 돌아온다. 시간 로그를 출력하고 결과를 반환한다.실행 순서 그림
동적 프록시 클래스 정보
dynamicA()와 dynamicB()둘을 동시에 함께 실행하면 JDK 동적 프록시가 각각 다른 동적 프록시 클래스를 만들어주는 것을 확인할 수 있다.정리
예제를 보면 AImpl, BImpl 각각 프록시를 만들지 않았다. 프록시는 JDK 동적 프록시를 사용해서 동적으로 만들고
TimeInvocationHandler는 공통으로 사용했다.JDK 동적 프록시 기술 덕분에 적용 대상 만큼 프록시 객체를 만들지 않아도 된다. 그리고 같은 부가 기능 로직을 한번만 개발해서 공통으로 적용할 수 잇다. 만약 적용 대상이 100개여도 동적 프록시를 통해서 생성하고 각각 필요한
InvocationHandler만 만들어서 넣어주면 된다.결과적으로 프록시 클래스를 수없이 만들어야 하는 문제도 해결하고 부가 기능 로직도 하나의 클래스에 모아서 단일 책임 원칙(SRP)도 지킬 수 있게 되었다.
JDK 동적 프록시 없이 직접 프록시를 만들어서 사용할 때와 JDK 동적 프록시를 사용할 때의 차이를 그림으로 비교해보자.
JDK 동적프록시 도입전 - 직접 프록시 생성
JDK 동적 프록시 도입 후
JDK 동적 프록시 - 적용1
JDK 동적 프록시는 인터페이스가 필수이기 때문에 V1 애플리케이션에만 적용할 수 있다.
LogTraceBasicHandler는InvocationHandler인터페이스를 구현해서 JDK 동적 프록시에서 사용된다.private final Object target: 프록시가 호출할 대상이다.String message = method.getDeclaringClass().getSimpleName() + "." ...LogTrace에 사용할 메세지이다. 프록시를 직접 개발할때는OrderController.request()와 같이 프록시마다 호출되는 클래스의 메서드 이름을 직접 남겼다. 이제는Method를 통해서 호출되는 메서드 정보와 클래스 정보를 동적으로 확인할 수 있기 때문에 이 정보를 사용하면 된다.동적 프록시를 사용하도록 수동 빈 등록을 설정하자.
Controller,Service,Repository에 맞는 동적 프록시를 생성해주면 된다.LogTraceBasicHandler: 동적 프록시를 만들더라도 LogTrace 를 출력하는 로직은 모두 같기때문에 프록시는 모두LogTraceBasicHandler를 사용한다.ProxyApplication - 수정
그림
남은 문제
JDK 동적 프록시 - 적용2
메서드 이름 필터 기능 추가
LogTraceFilterHandler
LogTraceFilterHandler는 기존 기능에 다음 기능이 추가되었다.LogTrace로직을 실행한다. 이름이 매칭되지 않으면 실제 로직을 바로 호출한다.스프링이 제공하는
PatternMatchUtils.simpleMatch(..)를 사용하면 단순한 매칭 로직을 쉽게 적용할 수 있다.String[] patterns: 적용할 패턴은 생성자를 통해서 외부에서 받는다.
DynamicProxyFilterConfig
ProxyApplication
실행
실행해보면 no-log 가 사용하는
noLog()메서드에는 로그가 남지 않는 것을 확인할 수 있다.JDK 동적 프록시 - 한계
JDK 동적 프록시는 인터페이스가 필수이다.
그렇다면 V2 애플리케이션처럼 인터페이스 없이 클래스만 있는 경우에는 어떻게 동적 프록시를 적용할 수 있을까? 이것은 일반적인 방법으로는 어렵고,
CGLIB라는 바이트 코드를 조작하는 특별한 라이브러리를 사용해야 한다.Beta Was this translation helpful? Give feedback.
All reactions