# Proxy / JDK Dynamic Proxy / Code Generator Library(CGLIB)
# 배경
- 토비의 스프링 책으로 AOP를 학습하다가 막혔는데 알고보니 프록시 대해서 잘 몰라서 막힌다는 걸 알게 되어 프록시에 관한 내용들을 글로 정리하면서 학습하고자 한다.
# 다루는 내용
- Proxy
- 프록시 패턴
- 동적 프록시
- JDK Dynamic Proxy
- Code Generator Library(CGLIB)
- Proxy Factory Bean
## Proxy
**특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 객체를 의미한다.**
- Client로부터 Target을 대신해서 요청을 받는 **대리인**
- 실제 오브젝트인 Target은 Proxy를 통해 최종적으로 요청받아 처리한다.
- 그 결과, Target은 자신의 기능에만 집중하고 **부가적인 기능**은 Proxy**에게 위임**한다.
> Client → Proxy → Target
>
### Proxy 사용 목적
두 가지로 구분할 수 있다.
- 1. 클라이언트가 Target에 **접근**하는 방법을 **제어**하기 위함 ex) JPA Hibernate 지연로딩
- 지연로딩은 어떻게 동작하나? 실제로 필요하기 전까지, 연관 관계로 가지고 있는 엔티티 필드에 null 값을 넣어둘 순 없으니 프록시 객체를 주입해서 실제 객체가 들어있는 것처럼 동작하도록 한다.
- 프록시 객체는 **실제 객체를 상속한 타입**을 가지고 있기 떄문에 문제없이 동작한다.
- 2.Target에 **부가적인 기능**을 주기 위함 ex) `@Transactional`
- 어떻게? Reflection API, 바이트 코드 조작 등의 방법으로 프록시를 구현해 부가적인 기능을 준다.
### Proxy를 직접 구현한다면?
- Proxy 패턴을 통해서 Proxy를 직접 구현할 수 있다.
### Proxy 패턴
- 특정 객체에 대한 **접근을 제어**하거나 **부가기능**을 구현하는데 사용하는 패턴

출처: https://en.wikipedia.org/wiki/Proxy_pattern
### Proxy 패턴을 사용함으로써 얻는 장점
**기능 추가, 접근 제어 등 다양하게 활용할 수 있다.**
- OCP(Open Closed Principle) 원칙을 지킨다.
- 기존 Target 코드를 변경하지 않고 새로운 **기능을 추가**할 수 있다.
- SRP(Single Responsibility Principle) 원칙을 지킨다.
- Validation 처리와 같은 부가적인 기능은 분리시킴으로써 기존 Target 코드가 해야하는 역할만 **유지**할 수 있다.
### Proxy 패턴을 사용함으로써 얻는 단점
- 코드의 복잡도 증가
- 프록시패턴을 구현하려면 인터페이스를 구현해야하고 또한 프록시 객체를 생성해서 구현해줘야 한다.
- 중복 코드 발생
- 부가기능을 구현할 때 모든 메서드에 적용해줘야 하기 때문에 중복 코드가 발생한다.
> **프록시 패턴의 문제점을 동적 프록시를 통해 해결할 수 있다.**
>
---
## 동적 프록시(Dynamic Proxy)
동적 프록시는 말그대로 동적인 시점(런타임 시점)에 프록시를 자동으로 만들어서 적용해주는 기술이다.
### 동적 프록시 기술이 만들어진 배경
- 프록시 패턴의 문제점으로 동적 프록시 기술이 만들어졌다.
- 프록시를 적용해야 할 클래스가 수십 개에서 수백 개가 되면?
- 클래스를 그 개수만큼 반복해서 만들어주어야 하는데 이런 불편함을 해소하기 위해 나온 기술
### 스프링에서 동적 프록시는 어떻게 동작할까?
- 클라이언트가 메소드를 요청하면 Proxy Factory Bean 이라는 곳에서 인터페이스 유무를 확인하여
- 인터페이스가 있으면 JDK Dynamic Proxy 방식으로 프록시를 생성하고,
- 인터페이스가 없으면 CGLIB의 방식으로 프록시를 생성한다.
## JDK Dynamic Proxy
동적 프록시의 한 종류이다.
- 프록시 클래스를 직접 구현하지 않아도 된다.
- 코드 복잡도 해소
- **`Invocatio Handler`**를 ******사용해서 중복 코드 제거를 제거한다.
- `Invocation Handler`는 **Reflection API**를 사용한다.
### **Invocation Handler**
- 실제 객체인 Target의 필드를 반드시 가지고 있어야 한다. → **Target에 의존적이다.**
- 왜 가지고 있어야할까? 부가기능을 수행하기 위해서
- Target A, Target B, Target C … 에 맞는 빈을 매번 생성해줘야 한다.
### **Reflection API**
- 구체적인 클래스 타입을 몰라도 런타임에 클래스의 정보에 접근할 수 있게 해주는 자바 API
### Reflection API 단점
- JVM 최적화가 동작하지 않아 성능상 **느리다.**
- JVM 최적화란? JVM이 실행하는 프로그램의 성능을 개선하기 위해 시스템 자원을 효율적으로 사용하도록 설정하는 것
- 최적화 방법
- 코드 변경: 불필요한 변수 할당을 제거하거나 반복문 내 임시 변수 사용을 없애면서 개발자가 작성한 코드를 직접 수정한다.
- JVM 설정 파일 변경: 메모리 제한, GC 동작, JIT컴파일러 동작 등을 조절할 수 있다.
- 메모리 최적화: 힙 메모리 크기 조절, GC 동작 설정, 클래스 로딩 최적화 등을 통해 메모리를 최적화할 수 있다.
- 코드 컴파일링: 어떤 메소드가 자주 호출되는지, 어떤 메소드가 오랜 시간을 차지하는지 등을 판별하면서 성능 저하가 발생하는 원인을 파악하고 최적화 작업 수행

출처: https://docs.oracle.com/javase/tutorial/reflect/
### JDK Dynamic Proxy 사용 흐름
> Client → JDK Dynamic Proxy → Invocation Handler → Target
>
- 클라이언트가 메소드를 요청하면 JDK Dynamic Proxy는 메소드 처리를 `Invocation Handler`에게 위임한다.
- `Invocation Handler`는 부가기능을 수행하고, 이후 Target의 로직을 처리하기 위해 Target에게 위임한다.
### JDK Dynamic Proxy 특징 정리
- JDK에서 지원하는 프록시 생성 방법
- Reflection API를 사용한다. → 외부 라이브러리에 의존하지 않지만 JVM 최적화가 동작하지 않아 느리다.
- **인터페이스**가 반드시 있어야 한다.
- `Invocation Handler`를 재정의한 **invoke**를 구현해줘야 **부가기능이 추가**된다.
### 스프링의 프록시 구현 - ProxyFactoryBean
- 스프링은 일관된 방법으로 프록시를 만들 수 있게 도와주는 추상 레이어를 제공
- JDK Dynamic Proxy를 **추상화**해서 적절한 방식의 프록시를 생성하도록 도와주는 팩토리 클래스다.
- 프록시 오브젝트를 생성해주는 기술을 추상화한 팩토리 빈을 제공
- ProxyFactoryBean : 프록시를 생성해서 빈 오브젝트로 등록하게 해주는 팩토리 빈
- 순수하게 프록시 생성을 위한 작업만을 담당하고, 부가 기능은 별도의 빈에 둘 수 있음
### ProxyFactoryBean 특징
- target의 인터페이스 정보가 필요없다.
- 프록시 빈을 생성해준다.
- 부가기능을 `MethodInterceptor`로 구현
- `MethodInterceptor` 를 재정의한 invoke를 구현해줘야한다.
- CGLIB의 MethodInterceptor 와는 다르다.
### ProxyFactoryBean의 프록시 생성 메커니즘
> ProxyFactoryBean → Proxy → MethodInterceptor → Target
>
- ProxyFactoryBean이 프록시를 생성하면 부가기능을 MethodInerceptor가 수행한다.
- InvoationHandler와는 달리, Target에 대한 정보는 Proxy가 가지고 있다.
### MethodInerceptor는 왜 Target을 가지지 않을까?
- 부가기능을 **독립적**으로 유지하기 위해서이다.
- 부가기능을 싱글톤으로 공유하여 사용 가능하다.
---
## Code Generator Library(CGLIB)
동적 프록시의 한 종류이다.
- 상속을 통한 프록시 구현
- 메소드가 처음 호출됐을 때 동적으로 Target 클래스의 **바이트코드를 조작해서 프록시를 구현한다.**
- 이후 호출 시엔 조작된 바이트 코드를 **재사용**한다
- 바이트 코드 조작은 어떻게 하나?
- `Enhancer` 인스턴스를 생성하여 런타임 시 해당 결과값을 선정한다.
- 메서드 시그니처에 따라서 intercept
- BeanGenerator의 GGLIB 사용
- `MethodInterceptor`를 재정의한 **intercept**를 구현해야 **부가기능**이 추가된다.
### CGLIB 사용 흐름
> client → CGLIB → MethodInterceptor → Target
>
- 클라이언트가 메소드를 요청하면 CGLIB은 메소드 처리를 `MethodInterceptor` 에게 위임한다.
- `MethodInterceptor` 는 부가기능을 수행하고, 이후 Target의 로직을 처리하기 위해 Target에게 위임한다.
### CGLIB 특징 정리
- 인터페이스에도 강제로 적용할 수 있다. 이때는 클래스에도 프록시를 적용시켜야 한다.
- 메서드에 `final` 을 붙이면 오버라이딩이 불가능하다.
- `~~net.sf.cglib.proxy.Enhancer` 의존성을 추가해야 한다.~~
- 3.2 version Spring Core 패키지에 포함
- ~~Defult 생성자 필요~~
- 4.0 version Objeansis 라이브러리
- ~~Target의 생성자 두 번 호출~~
- 4.0 version Objeansis 라이브러리
### CGLIB 단점
- 상속에 따른 제약이 존재한다.
## JDK Dynamic Proxy와 CGLIB 비교
- JDK Dynamic Proxy
- 리플렉션을 사용해서 느리다.
- 인터페이스가 있어야 동작한다.
- CGLIB
- 바이크코드를 조작해서 빠르다.
- 클래스만 있어도 동작한다.
- 상속을 이용해서 프록시를 만든다.
- 메서드에 final을 붙이면 안된다.
→ **Spring 4.3 & spring boot 1.4 이후 CGLIB proxy가 기본 설정이다.**
### 스프링과 스프링부트에서 CGLIB을 기본 설정으로 한 이유는?
 developer and co-creator of the [Spring Boot](http://projects.spring.io/spring-boot/) project.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c5c98b67-497f-4815-95c8-cd87b0a09c31/Untitled.png)
Phil Webb is a [Spring Framework](http://projects.spring.io/spring-framework/) developer and co-creator of the [Spring Boot](http://projects.spring.io/spring-boot/) project.
- 인터페이스 기반 프록시는 ClassCast Exceptions를 추적하기 어렵게 하기 때문이라고 한다.
# 마치면서
- 프록시에 대해서 전체적으로 정리했다. 쉽지는 않다. 다음에는 Spring AOP와`@Transactional` 이 어떻게 동작하는지 학습하면서 다시 복습을 해봐야겠다.# Proxy / JDK Dynamic Proxy / Code Generator Library(CGLIB)
# 배경
- 토비의 스프링 책으로 AOP를 학습하다가 막혔는데 알고보니 프록시 대해서 잘 몰라서 막힌다는 걸 알게 되어 프록시에 관한 내용들을 글로 정리하면서 학습하고자 한다.
# 다루는 내용
- Proxy
- 프록시 패턴
- 동적 프록시
- JDK Dynamic Proxy
- Code Generator Library(CGLIB)
- Proxy Factory Bean
## Proxy
**특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 객체를 의미한다.**
- Client로부터 Target을 대신해서 요청을 받는 **대리인**
- 실제 오브젝트인 Target은 Proxy를 통해 최종적으로 요청받아 처리한다.
- 그 결과, Target은 자신의 기능에만 집중하고 **부가적인 기능**은 Proxy**에게 위임**한다.
> Client → Proxy → Target
>
### Proxy 사용 목적
두 가지로 구분할 수 있다.
- 1. 클라이언트가 Target에 **접근**하는 방법을 **제어**하기 위함 ex) JPA Hibernate 지연로딩
- 지연로딩은 어떻게 동작하나? 실제로 필요하기 전까지, 연관 관계로 가지고 있는 엔티티 필드에 null 값을 넣어둘 순 없으니 프록시 객체를 주입해서 실제 객체가 들어있는 것처럼 동작하도록 한다.
- 프록시 객체는 **실제 객체를 상속한 타입**을 가지고 있기 떄문에 문제없이 동작한다.
- 2.Target에 **부가적인 기능**을 주기 위함 ex) `@Transactional`
- 어떻게? Reflection API, 바이트 코드 조작 등의 방법으로 프록시를 구현해 부가적인 기능을 준다.
### Proxy를 직접 구현한다면?
- Proxy 패턴을 통해서 Proxy를 직접 구현할 수 있다.
### Proxy 패턴
- 특정 객체에 대한 **접근을 제어**하거나 **부가기능**을 구현하는데 사용하는 패턴

출처: https://en.wikipedia.org/wiki/Proxy_pattern
### Proxy 패턴을 사용함으로써 얻는 장점
**기능 추가, 접근 제어 등 다양하게 활용할 수 있다.**
- OCP(Open Closed Principle) 원칙을 지킨다.
- 기존 Target 코드를 변경하지 않고 새로운 **기능을 추가**할 수 있다.
- SRP(Single Responsibility Principle) 원칙을 지킨다.
- Validation 처리와 같은 부가적인 기능은 분리시킴으로써 기존 Target 코드가 해야하는 역할만 **유지**할 수 있다.
### Proxy 패턴을 사용함으로써 얻는 단점
- 코드의 복잡도 증가
- 프록시패턴을 구현하려면 인터페이스를 구현해야하고 또한 프록시 객체를 생성해서 구현해줘야 한다.
- 중복 코드 발생
- 부가기능을 구현할 때 모든 메서드에 적용해줘야 하기 때문에 중복 코드가 발생한다.
> **프록시 패턴의 문제점을 동적 프록시를 통해 해결할 수 있다.**
>
---
## 동적 프록시(Dynamic Proxy)
동적 프록시는 말그대로 동적인 시점(런타임 시점)에 프록시를 자동으로 만들어서 적용해주는 기술이다.
### 동적 프록시 기술이 만들어진 배경
- 프록시 패턴의 문제점으로 동적 프록시 기술이 만들어졌다.
- 프록시를 적용해야 할 클래스가 수십 개에서 수백 개가 되면?
- 클래스를 그 개수만큼 반복해서 만들어주어야 하는데 이런 불편함을 해소하기 위해 나온 기술
### 스프링에서 동적 프록시는 어떻게 동작할까?
- 클라이언트가 메소드를 요청하면 Proxy Factory Bean 이라는 곳에서 인터페이스 유무를 확인하여
- 인터페이스가 있으면 JDK Dynamic Proxy 방식으로 프록시를 생성하고,
- 인터페이스가 없으면 CGLIB의 방식으로 프록시를 생성한다.
## JDK Dynamic Proxy
동적 프록시의 한 종류이다.
- 프록시 클래스를 직접 구현하지 않아도 된다.
- 코드 복잡도 해소
- **`Invocatio Handler`**를 ******사용해서 중복 코드 제거를 제거한다.
- `Invocation Handler`는 **Reflection API**를 사용한다.
### **Invocation Handler**
- 실제 객체인 Target의 필드를 반드시 가지고 있어야 한다. → **Target에 의존적이다.**
- 왜 가지고 있어야할까? 부가기능을 수행하기 위해서
- Target A, Target B, Target C … 에 맞는 빈을 매번 생성해줘야 한다.
### **Reflection API**
- 구체적인 클래스 타입을 몰라도 런타임에 클래스의 정보에 접근할 수 있게 해주는 자바 API
### Reflection API 단점
- JVM 최적화가 동작하지 않아 성능상 **느리다.**
- JVM 최적화란? JVM이 실행하는 프로그램의 성능을 개선하기 위해 시스템 자원을 효율적으로 사용하도록 설정하는 것
- 최적화 방법
- 코드 변경: 불필요한 변수 할당을 제거하거나 반복문 내 임시 변수 사용을 없애면서 개발자가 작성한 코드를 직접 수정한다.
- JVM 설정 파일 변경: 메모리 제한, GC 동작, JIT컴파일러 동작 등을 조절할 수 있다.
- 메모리 최적화: 힙 메모리 크기 조절, GC 동작 설정, 클래스 로딩 최적화 등을 통해 메모리를 최적화할 수 있다.
- 코드 컴파일링: 어떤 메소드가 자주 호출되는지, 어떤 메소드가 오랜 시간을 차지하는지 등을 판별하면서 성능 저하가 발생하는 원인을 파악하고 최적화 작업 수행

출처: https://docs.oracle.com/javase/tutorial/reflect/
### JDK Dynamic Proxy 사용 흐름
> Client → JDK Dynamic Proxy → Invocation Handler → Target
>
- 클라이언트가 메소드를 요청하면 JDK Dynamic Proxy는 메소드 처리를 `Invocation Handler`에게 위임한다.
- `Invocation Handler`는 부가기능을 수행하고, 이후 Target의 로직을 처리하기 위해 Target에게 위임한다.
### JDK Dynamic Proxy 특징 정리
- JDK에서 지원하는 프록시 생성 방법
- Reflection API를 사용한다. → 외부 라이브러리에 의존하지 않지만 JVM 최적화가 동작하지 않아 느리다.
- **인터페이스**가 반드시 있어야 한다.
- `Invocation Handler`를 재정의한 **invoke**를 구현해줘야 **부가기능이 추가**된다.
---
## Code Generator Library(CGLIB)
동적 프록시의 한 종류이다.
- 상속을 통한 프록시 구현
- 메소드가 처음 호출됐을 때 동적으로 Target 클래스의 **바이트코드를 조작해서 프록시를 구현한다.**
- 이후 호출 시엔 조작된 바이트 코드를 **재사용**한다
- 바이트 코드 조작은 어떻게 하나?
- `Enhancer` 인스턴스를 생성하여 런타임 시 해당 결과값을 선정한다.
- 메서드 시그니처에 따라서 intercept
- BeanGenerator의 GGLIB 사용
- `MethodInterceptor`를 재정의한 **intercept**를 구현해야 **부가기능**이 추가된다.
### CGLIB 사용 흐름
> client → CGLIB → MethodInterceptor → Target
>
- 클라이언트가 메소드를 요청하면 CGLIB은 메소드 처리를 `MethodInterceptor` 에게 위임한다.
- `MethodInterceptor` 는 부가기능을 수행하고, 이후 Target의 로직을 처리하기 위해 Target에게 위임한다.
### CGLIB 특징 정리
- 인터페이스에도 강제로 적용할 수 있다. 이때는 클래스에도 프록시를 적용시켜야 한다.
- 메서드에 `final` 을 붙이면 오버라이딩이 불가능하다.
- `~~net.sf.cglib.proxy.Enhancer` 의존성을 추가해야 한다.~~
- 3.2 version Spring Core 패키지에 포함
- ~~Defult 생성자 필요~~
- 4.0 version Objeansis 라이브러리
- ~~Target의 생성자 두 번 호출~~
- 4.0 version Objeansis 라이브러리
### CGLIB 단점
- 상속에 따른 제약이 존재한다.
## JDK Dynamic Proxy와 CGLIB 비교
- JDK Dynamic Proxy
- 리플렉션을 사용해서 느리다.
- 인터페이스가 있어야 동작한다.
- CGLIB
- 바이크코드를 조작해서 빠르다.
- 클래스만 있어도 동작한다.
- 상속을 이용해서 프록시를 만든다.
- 메서드에 final을 붙이면 안된다.
→ **Spring 4.3 & spring boot 1.4 이후 CGLIB proxy가 기본 설정이다.**
### 스프링과 스프링부트에서 CGLIB을 기본 설정으로 한 이유는?
 developer and co-creator of the [Spring Boot](http://projects.spring.io/spring-boot/) project.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c5c98b67-497f-4815-95c8-cd87b0a09c31/Untitled.png)
Phil Webb is a [Spring Framework](http://projects.spring.io/spring-framework/) developer and co-creator of the [Spring Boot](http://projects.spring.io/spring-boot/) project.
- 인터페이스 기반 프록시는 ClassCast Exceptions를 추적하기 어렵게 하기 때문이라고 한다.
### 스프링의 프록시 구현 - ProxyFactoryBean
- 스프링은 일관된 방법으로 프록시를 만들 수 있게 도와주는 추상 레이어를 제공
- JDK Dynamic Proxy를 **추상화**해서 적절한 방식의 프록시를 생성하도록 도와주는 팩토리 클래스다.
- 프록시 오브젝트를 생성해주는 기술을 추상화한 팩토리 빈을 제공
- ProxyFactoryBean : 프록시를 생성해서 빈 오브젝트로 등록하게 해주는 팩토리 빈
- 순수하게 프록시 생성을 위한 작업만을 담당하고, 부가 기능은 별도의 빈에 둘 수 있음
### ProxyFactoryBean 특징
- target의 인터페이스 정보가 필요없다.
- 프록시 빈을 생성해준다.
- 부가기능을 `MethodInterceptor`로 구현
- `MethodInterceptor` 를 재정의한 invoke를 구현해줘야한다.
- CGLIB의 MethodInterceptor 와는 다르다.
### ProxyFactoryBean의 프록시 생성 메커니즘
> ProxyFactoryBean → Proxy → MethodInterceptor → Target
>
- ProxyFactoryBean이 프록시를 생성하면 부가기능을 MethodInerceptor가 수행한다.
- InvoationHandler와는 달리, Target에 대한 정보는 Proxy가 가지고 있다.
### MethodInerceptor는 왜 Target을 가지지 않을까?
- 부가기능을 **독립적**으로 유지하기 위해서이다.
- 부가기능을 싱글톤으로 공유하여 사용 가능하다.
# 마치면서
- 프록시에 대해서 전체적으로 정리했다. 쉽지는 않다. 다음에는 Spring AOP와`@Transactional` 이 어떻게 동작하는지 학습하면서 다시 복습을 해봐야겠다.