[Velog ] Java Dynamic Proxy vs CGlib
What is a Proxy?Dynamic ProxyProxy๋ฅผ ๋ฐํ์์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ (JDK Dynamic Proxy)JpaRepository์ ์CGLib(Code Generator Library)
What is a Proxy?
- ์ด๋ฏธ ์กด์ฌํ๋ ํด๋์ค์ ์ด๋ค ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ฑฐ๋ ์์ ํ ๋, ํ๋ก์ ์ค๋ธ์ ํธ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํจ
- ํ๋ก์ ์ค๋ธ์ ํธ๋ ํ๊ฒ ์ค๋ธ์ ํธ ๋์ ํด๋ผ์ด์ธํธ์์ ์ฌ์ฉ๋จ
- ์ผ๋ฐ์ ์ผ๋ก ํ๋ก์ ์ค๋ธ์ ํธ๋ ํ๊น ์ค๋ธ์ ํธ์ ๋์ผํ ๋ฉ์๋๋ฅผ ๊ฐ์ง๋ฉฐ(๊ฐ์ ์ธํฐํ์ด์ค ์ด๋ฏ๋ก), ์๋ฐ ํ๋ก์ ํด๋์ค์์๋ ๋๊ฐ ์๋ณธ ํด๋์ค๋ฅผ ํ์ฅํจ
- ํ๋ก์๋ ์๋ ์ค๋ธ์ ํธ(
target
)์ ๋ํ ์ ์ด๊ถ์ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ๋ฉ์๋๋ฅผ ํธ์ถ ํ ์ ์๋ค
ํ๋ก์ ํด๋์ค๋ ๋ง์ ๊ฒ๋ค์ ์๋ ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ํธ๋ฆฌํ๊ฒ ๊ตฌํํ ์ ์๋ค
- ๋ฉ์๋๊ฐ ์์ํ๊ณ ๋๋ ๋ ๋ก๊ทธ๋ฅผ ๋จ๊น
- argument์ ๋ํ ์ถ๊ฐ์ ์ธ ํ์ธ์ ์ํ
- ์๋ ํด๋์ค์ ํ๋์ ๋ชจํนํ๋ค
- ๋น์ผ ์์์ ๋ํ Lazy ์ ๊ทผ์ ์คํ
- ํ๋ก์๋ ์ฌ์ฉ ๋ชฉ์ ์ ๋ฐ๋ผ ๋ ๊ฐ์ง๋ก ๊ตฌ๋ถํ ์ ์์
1. ํด๋ผ์ด์ธํธ๊ฐ ํ๊น์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์ ์ดํ๊ธฐ ์ํด์ โ
Proxy pattern
๋ฐ์ฝ๋ ์ดํฐ ํจํด๊ณผ ๋ฌ๋ฆฌ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ์ถ๊ฐํ์ง๋ ์์
2. ํ๊น์ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๋ถ์ฌํด์ฃผ๊ธฐ ์ํด์ โ
Decorator Pattern
Proxy vs Proxy Pattern
์ผ๋ฐ์ ์ผ๋ก ๋ถ๋ฅด๋ Proxy๋ ์ค์ Target์ ๊ธฐ๋ฅ์ ์ํํ๋ฉด์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ์ถ๊ฐํ๋ ์ค์ โ๊ฐ์ฒดโ๋ฅผ ์๋ฏธ
Proxy Pattern์ ์ค์ ๋ก Target์ ๋ํ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ์ถ๊ฐํ์ง ์๊ณ ๊ทธ์ ํด๋ผ์ด์ธํธ ํ๊น์ ์ ๊ทผํ๋ ๋ฐฉ์์ ๋ณ๊ฒฝํด์ฃผ๋ ์ญํ ์ ํจ
Dynamic Proxy
- ๋ฐํ์ ์ค์ ํ๋ก์ ๊ฐ์ฒด์ ํ๋ก์ ํด๋์ค๊ฐ ์์ฑ๋๋ ์ํฉ์๋ง ์ ์ฉ๋๋ ๊ฒ์ด ์๋์๋ ๋ถ๊ตฌํ๊ณ , Java์์ ๋งค์ฐ ํฅ๋ฏธ๋ก์ด ์ฃผ์ ์
- ์ด ์ฃผ์ ๋ reflection class, ๋ฐ์ดํธ ์ฝ๋ ์กฐ์, ๋๋ ๋์ ์ผ๋ก ์์ฑ๋ ์๋ฐ์ฝ๋๋ฅผ ์ปดํ์ผํ๋ ๋ฑ์ ์ฌ์ฉ์ด ํ์ํ๊ธฐ์ ๊ณ ๊ธ ์ฃผ์ ์ด๋ค.
- ๋ฐํ์ ์ค์ ๋ฐ์ดํธ์ฝ๋๊ฐ ์ด์ฉ์ด ๋ถ๊ฐ๋ฅํ ์ ํด๋์ค๋ฅผ ๊ฐ์ง๋ ค๋ฉด ๋ฐ์ดํธ์ฝ๋ ์์ฑ ํน์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ๋ก๋ํ ์ ์๋ ํด๋์ค ๋ก๋ ๋ํ ํ์ํจ
- ๋ฐ์ดํธ์ฝ๋ ์์ฑ์ ์ํด CGLib๊ณผ bytebuddy, ๋ด์ฅ Java ์ปดํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํ ์ ์์
- ํ๋ก์ ํด๋์ค์ ํด๋์ค๊ฐ ํธ์ถํ๋ ํธ๋ค๋ฌ(
InvocationHandler
)๋ฅผ ์๊ฐํด๋ณด๋ฉด, ์ ์ฑ ์์ ๋ถ๋ฆฌ๊ฐ ์ค์ํ์ง ์ดํดํ ์ ์์ - Proxy ํด๋์ค๋ ๋ฐํ์์ ์์ฑ๋์ง๋ง Proxy ํด๋์ค์ ์ํด ํธ์ถ๋๋ ํธ๋ค๋ฌ๋ ์ผ๋ฐ ์์ค์ฝ๋์ ์ฝ๋ฉ๋์ด ์๊ณ ์ ์ฒด ํ๋ก๊ทธ๋จ์ด ์ปดํ์ผ ๋ ๋ ๊ฐ์ด ์ปดํ์ผ ๋๊ธฐ ๋๋ฌธ์
Proxy๋ฅผ ๋ฐํ์์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ (JDK Dynamic Proxy)
java.lang.reflection.Proxy
ํด๋์ค ์ด์ฉ
- ํด์ผํ ์ผ์ ํ๋ก์ ์ค๋ธ์ ํธ๊ฐ ํธ์ถํ ์ ์๋๋ก
java.lang.InvocationHandler
๋ฅผ ๊ตฌํํ๋ ๊ฒ InvocationHandler
์ธํฐํ์ด์ค๋invoke()
๋ผ๋ ๋จ ํ๋์ ๋ฉ์๋๋ง ๊ฐ์งinvoke()
๋ฉ์๋ ํธ์ถ ์, argument = (ํ๋ก์๋ ์๋ณธ ์ค๋ธ์ ํธ, ํธ์ถ๋ ๋ฉ์๋(๋ฆฌํ๋ ์ Method
์ค๋ธ์ ํธ), ์๋ณธ argument)
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkProxyDemo { interface If { void originalMethod(String s); } static class Original implements If { @Override public void originalMethod(String s) { System.out.println(s); } } static class Handler implements InvocationHandler { private final If original; public Handler(If original) { this.original = original; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("BEFORE"); method.invoke(original, args); System.out.println("AFTER"); return null; } } public static void main(String[] args){ Original original = new Original(); Handler handler = new Handler(original); If f = (If) Proxy.newProxyInstance( If.class.getClassLoader(), // ๋์ ์ผ๋ก ์์ฑ๋๋ ๋ค์ด๋๋ฏน ํ๋ก์ ํด๋์ค์ ๋ก๋ฉ์ ์ฌ์ฉ๋ ํด๋์ค ๋ก๋ new Class[] {If.class}, // ํด๋น Proxy๊ฐ ๊ฐ์ ธ์ผํ Interface ํ์ handler // InvoacationHandler๋ฅผ ๊ตฌํํ ํด๋์ค (๋ถ๊ฐ๊ธฐ๋ฅ + ์์ ์ญํ ๋ด๋น) ); f.originalMethod("Hallo"); } }


- ํธ๋ค๋ฌ(
InvocationHandler
์ ๊ตฌํ์ฒด)๊ฐ ์๋ณธ ๊ฐ์ฒด์ ๋ํด ์๋ณธ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ค๋ฉด, ๊ทธ๊ฒ์ ๋ํ ์ก์ธ์ค ๊ถํ์ด ์์ด์ผ ํจ. ์๋ณธ ๊ฐ์ฒด(Original,target
)๊ฐInvocationHandler
์ ์ธ์๋ก ์ ๋ฌ Handler handler = new Handler(original);
์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ์๋ณธ ํด๋์ค์ ๋ํด ๋ณ๋์ ํธ๋ค๋ฌ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ ์ ์์
- ํน๋ณํ ๊ฒฝ์ฐ์ ํธ์ถ ํธ๋ค๋ฌ์ ์๋ณธ ์ค๋ธ์ ํธ๊ฐ ์๋ ์ธํฐํ์ด์ค ํ๋ก์๋ฅผ ๋ง๋ค ์ ์์. ๋ํ ์์ค์ฝ๋ ๋ด์์ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ํด๋์ค๋ ํ์ํ์ง ์์. ๋์ ์ผ๋ก ์์ฑ๋ ํ๋ก์ ํด๋์ค๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํ
JpaRepository์ ์
package com.uplus.virtualoffice.domain; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; public class ProxyTest { interface SimpleRepositoryInterface { User findById(Long id); void save(User user); } static class SimpleJpaRepository implements SimpleRepositoryInterface { private static Long key = 1L; private static final Map<Long, User> data = new HashMap<>(); @Override public User findById(Long id) { return data.get(id); } @Override public void save(User user) { data.put(key++, user); } } interface CustomRepository extends SimpleRepositoryInterface {} static class Handler implements InvocationHandler { private final SimpleRepositoryInterface original; public Handler(SimpleRepositoryInterface original) { this.original = original; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before"); Object ret = method.invoke(original, args); System.out.println("After"); return ret; } } @Test void name() { SimpleRepositoryInterface original = new SimpleJpaRepository(); Handler handler = new Handler(original); CustomRepository f = (CustomRepository)Proxy.newProxyInstance(CustomRepository.class.getClassLoader(), new Class[] {CustomRepository.class}, handler); f.save(new User("testuser@gmail.com", "asdfasdf123!", Role.ROLE_USER)); f.save(new User("testuser2@gmail.com", "asdfasdf123!", Role.ROLE_USER)); User user = f.findById(2L); System.out.println(user.getEmail()); } }

CGLib(Code Generator Library)
[Github ] CGLib
- ์ฝ๋ ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ๋ฐํ์์ ๋์ ์ผ๋ก ์๋ฐ ํด๋์ค์ ํ๋ก์๋ฅผ ์์ฑํด์ฃผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํจ
- ์์ Java JDK ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด ์๋๋ฏ๋ก CGLib์ด๋ผ๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํจ์ผ๋ก์จ ์ฌ์ฉํ ์ ์์
- ์ค์ CGLib์ Enhancer๋ผ๋ ํด๋์ค๋ฅผ ๋ฐํ์ผ๋ก ํ๋ก์๋ฅผ ์์ฑํ๋ฉฐ, ์ธํฐํ์ด์ค๊ฐ ์๋ ํด๋์ค์ ๋ํด์ ๋์ ํ๋ก์๋ฅผ ์์ฑํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ค์ํ ํ๋ก์ ํธ์์ ๋๋ฆฌ ์ฌ์ฉ๋๊ณ ์์
- Hibernate๋ ์๋ฐ๋น ๊ฐ์ฒด์ ๋ํ ํ๋ก์๋ฅผ ์์ฑํ ๋ CGLib์ ์ฌ์ฉํ๋ฉฐ, Spring์ ํ๋ก์ ๊ธฐ๋ฐ Aop๋ฅผ ๊ตฌํํ ๋ CGLib์ ์ฌ์ฉํ๊ณ ์์
- CGLib ํ๋ก์๋ Target Class๋ฅผ ์์๋ฐ์ ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๋ Proxy ์์ฑ์ ์ํด ๊ตณ์ด Interface๋ฅผ ๋ง๋๋ ์๊ณ ๋ฅผ ๋ ์ ์๊ฒ ๋จ
- โ ์์์ ์ด์ฉํ๊ธฐ์,
final
ํน์private
๋ฉ์๋, ํ๋์ ๋ํด ์ค๋ฒ๋ผ์ด๋ฉ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ Proxy์์ ํด๋น ๋ฉ์๋์ ๋ํ Aspect๋ฅผ ์ ์ฉํ ์ ์๊ฒ ๋จ
- CGLib Proxy์ ๊ฒฝ์ฐ ์ค์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํ์ฌ JDK Dynamic Proxy ๋ณด๋ค๋ ํผํฌ๋จผ์ค๊ฐ ์๋์ ์ผ๋ก ๋น ๋ฅธ ์ฅ์ ์ด ์์