목차
- Spring 핵심원리 고급편 - CGLIB
- Spring 핵심원리 고급편 - Dynamic Proxy 2
- Spring 핵심원리 고급편 - Dynamic Proxy 1
- Spring 핵심원리 고급편 - 리플렉션
- Spring 핵심원리 고급편 - 구체 클래스 기반 프록시 적용 2
- Spring 핵심원리 고급편 - 구체 클래스 기반 프록시
- Spring 핵심원리 고급편 - 인터페이스 프록시 1
- Spring 핵심원리 고급편 - Decorator Pattern 2
- Spring 핵심원리 고급편 - Decorator Pattern 1
- Spring 핵심원리 고급편 - Proxy 패턴 컴포넌트 스캔으로 자동 빈 등록
- Spring 핵심원리 고급편 - Proxy 패턴 인터페이스 없는 없는 구체 클래스
- Spring 핵심원리 고급편 - Proxy 패턴 인터페이스와 구체 클래스
- Spring 핵심원리 고급편 - Proxy 패턴
- Spring 핵심원리 고급편 - Strategy 패턴
- Spring 핵심원리 고급편 - Template Method 패턴
JDK 동적 프록시
JDK 동적 프록시는 인터페이스 를 기반으로 프록시 를 동적으로 만들어준다.
JDK 동적 프록시는 자바 리플렉션(Reflection) 을 이용하여 런타임 시에 인터페이스를 구현하는 프록시 객체 를 생성하는 기술
동적 프록시 기술을 사용하면 개발자가 직접 Proxy 클래스를 생성할 필요가 없이 런타임시 리플랙션의 Proxy 클래스가 동적으로 생성해준다는 장점이 있다. 하지만, 문제가 발생하면 런타임 에러 가 발생하므로 컴파일 시 오류를 찾기가 어려운 문제점 또한 존재한다.
- 동적 프록시 기술을 사용하면 런타임시 프록시 객체 를 생성해준다.
- 리플렉션 을 이용해 프록시를 생성한다.
- 타겟 인터페이스와 동일한 형태로 생성
- FactoryBean 을 통해서 생성
프록시 객체는 원본 객체의 대리자 역할을 하며, 클라이언트가 프록시 객체를 통해 원본 객체에 접근하면, 프록시 객체가 요청을 가로채서 필요한 전처리나 후처리를 수행한 후, 최종 결과를 반환합니다.
동적 프록시는 프로그래머가 직접 코드를 작성하지 않아도 인터페이스의 메서드 호출을 가로채서 처리할 수 있어서 매우 유용합니다. 예를 들어, AOP(Aspect Oriented Programming)에서는 동적 프록시를 이용해서 메서드 호출 전후에 로그를 남기거나, 보안 검사를 수행하거나, 트랜잭션 관리를 수행할 수 있습니다.
JDK에서는 java.lang.reflect 패키지에서 Proxy 클래스 를 제공합니다. 이 클래스의 정적 메서드인 newProxyInstance() 메서드를 이용하면, 인터페이스와 InvocationHandler 인터페이스를 구현한 클래스를 전달하여 동적 프록시 객체를 생성할 수 있습니다. 이때, InvocationHandler 인터페이스를 구현한 클래스에서는 invoke() 메서드를 구현하여 원본 객체의 메서드 호출을 가로채서 필요한 작업을 수행하도록 구현합니다.
newProxyInstance
Proxy 클래스 내 정적 메소드로 newProxyInstance 를 이용해 런타임 시 동적으로 프록시 객체 를 생성 후 반환한다.
전달 받은 인터페이스 를 기반으로 Proxy 클래스를 생성한 후 JVM 에 올리고 Proxy 객체를 반환한다.
newProxyInstance 메소드는 세 개의 인자를 받습니다. 첫 번째 인자는 동적 프록시 객체를 생성할 클래스 로더(ClassLoader) 입니다. 두 번째 인자는 동적 프록시 객체가 구현해야 할 인터페이스 목록 입니다. 세 번째 인자는 InvocationHandler 인터페이스를 구현한 객체 입니다.
newProxyInstance 메소드는 프록시 객체를 생성하여 반환합니다. 이 프록시 객체는 인터페이스 목록에 선언된 모든 메소드를 구현하며, 실제 호출 시 InvocationHandler에서 정의한 로직이 수행됩니다.
- ClassLoader
- Proxy 클래스를 JVM 으로 로드할 클래스 로더, null 을 전달할 시 기본 클래스 로더 사용
- Class<?>
- 프록시 클래스가 구현할 인터페이스 목록(배열)
- InvocationHandler
- 메서드가 호출되었을때 실행될 Handler
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) |
InvocationHandler
InvocationHandler 를 이용해 JDK 동적 프로시에 적용할 공통 로직 을 개발한다.
InvocationHandler 인터페이스는 invoke 메소드 하나만을 가지고 있으며, 이 메소드는 동적 프록시 객체가 처리해야 할 모든 메소드 호출을 가로채서 처리할 수 있습니다.
invoke 메소드는 세 개의 인자를 받습니다. 첫 번째 인자는 동적 프록시 객체 자체이며, 두 번째 인자는 원본 객체의 메소드입니다. 세 번째 인자는 원본 객체의 메소드 호출 시 전달된 인자 배열입니다.
InvocationHandler를 구현한 객체에서는 invoke 메소드를 구현하여 프록시 객체가 요청을 가로챌 때 처리해야 할 로직을 구현합니다. 예를 들어, 메소드 호출 전후에 로그를 남기거나, 메소드 호출 시간을 측정하거나, 트랜잭션을 관리하는 등의 작업을 수행할 수 있습니다.
InvocationHandler.java
public interface InvocationHandler { |
Dynamic Proxy 생성
InvocationHandler 인터페이스를 이용해 프록시 객체 생성시 추가할 로직을 정의 한다.
TimeInvocationHandler 클래스는 Target 클래스를 호출할 때 시작과 종료 로그를 찍어주는 공통 로직을 가지고 있다.
|
newProxyInstance
를 이용해 동적으로 Proxy 객체를 만들어준다.
public interface AInterface { |
|
newProxyInstance 를 이용해 동적으로 Proxy 객체를 생성
newProxyInstance 메소드를 이용해 실제 객체와 동일한 인터페이스 와 부가로직 이 추가된 프록시 객체를 생성한다.
newProxyInstance 메소드는 AInterface 인터페이스를 이용해 시작과 종료 로그 를 찍어주는 프록시 객체를 생성한 후 반환한다. 반환된 Proxy 객체는 AInterface 인터페이스를 구현한 객체로 실제 객체와 동일한 메소드를 이용해 호출했을때 시작, 종료 로그가 찍히는 것을 확인할 수 있다.
|
프록시 객체 호출 결과
프록시 객체를 호출했을 때 AImpl 객체를 호출하기 전에 시작, 종료 로그가 찍히는 것을 확인할 수 있다.
17:43:18.808 [Test worker] INFO hello.proxy.jdkdynamic.code.TimeInvocationHandler - TimeProxy 실행 |
public interface BInterface { |
|
|
17:49:03.329 [Test worker] INFO hello.proxy.jdkdynamic.code.TimeInvocationHandler - TimeProxy 실행 |