JDK Dynamic Proxy์ CGLIB
Spring AOP์ ํ๋ก์ ์์ฑ ๋ฐฉ์์ ๋ํด ๊ฐ๋ตํ ์์๋ด ์๋ค.

๐กProxyFactory
์คํ๋ง AOP์ ProxyFactory๋ ํ๋ก์๋ฅผ ์์ฑํ๋ ๊ณผ์ ์์ ํ๊ฒ์ด ํ๋ ์ด์์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์๋ค๋ฉด JDK Dynamic Proxy ๋ฐฉ์์ผ๋ก ํ๋ก์๊ฐ ์์ฑํ๊ณ , ๊ทธ๊ฒ ์๋๋ผ๋ฉด CGLIB ๋ฐฉ์์ผ๋ก ํ๋ก์๊ฐ ์์ฑํด์ฃผ๋ ํฉํ ๋ฆฌ์ ๋๋ค.

ํด๋ผ์ด์ธํธ๊ฐ ProxyFactory๋ก ์์ฑ๋ ํ๋ก์๋ฅผ ํธ์ถํ๋ฉด JDK Dynamic Proxy์ ๊ฒฝ์ฐ InvocationHandler์ invoke() ๋ฉ์๋๊ฐ ์คํ๋๊ณ , CGLIB์ ๊ฒฝ์ฐ MethodInterceptor์ intercept() ๋ฉ์๋๊ฐ ์คํ๋ฉ๋๋ค. ์ดํ Advice๋ฅผ ํธ์ถํ๋ฉด์ Target์ ๋ํ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ์ฉ์ํฌ ์ ์๊ฒ ๋ฉ๋๋ค.
๐กJDK Dynamic Proxy
public interface MyService {
void doSomething();
}
@Service
public class MyServiceImpl implements MyService {
@Override
public void doSomething() {
// do something
}
}
MyServiceImpl์ doSomething() ๋ฉ์๋ ํธ์ถ ์ ํ๋ก ๋ก๊ทธ๋ฅผ ์ฐ๊ธฐ ์ํด, JDK Dynamic Proxy๋ฅผ ์ด์ฉํ์ฌ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. JDK Dynamic Proxy๋ Reflection API๋ฅผ ์ฌ์ฉํฉ๋๋ค. Proxy ํด๋์ค์ newProxyInstance๋ก ํ๋ก์๋ฅผ ์์ฑํ๋๋ฐ, ์๋์ ๊ฐ์ด ํ๊ฒ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ํ๋ก์๋ฅผ ์์ฑํ๊ฒ ๋ฉ๋๋ค.
public class DynamicInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MyService realSubject = new MyServiceImpl();
log.debug("๋ฉ์๋ ํธ์ถ ์ด์ ");
Object invoke = method.invoke(realSubject, args);
log.debug("๋ฉ์๋ ํธ์ถ ์ดํ");
return invoke;
}
}
MyService myService = (MyService) Proxy.newProxyInstance(
MyService.class.getClassLoader(), // ํด๋์ค๋ก๋
new Class[]{MyService.class}, // ํ๊ฒ์ ์ธํฐํ์ด์ค
new DynamicInvocationHandler() // ํ๊ฒ์ ์ ๋ณด๊ฐ ํฌํจ๋ Handler
);
InvocationHandler๋ ํจ์ํ ์ธํฐํ์ด์ค์ด๊ธฐ ๋๋ฌธ์ ๋๋ค์์ ์ด์ฉํ์ฌ ์ธ๋ผ์ธ์ผ๋ก ์์ฑํ ์๋ ์์ต๋๋ค.
MyService myService = (MyService) Proxy.newProxyInstance(
MyService.class.getClassLoader(), // ํด๋์ค๋ก๋
new Class[]{MyService.class}, // ํ๊ฒ์ ์ธํฐํ์ด์ค
((proxy, method, args) -> {
MyService realSubject = new MyServiceImpl();
log.debug("๋ฉ์๋ ํธ์ถ ์ด์ ");
Object invoke = method.invoke(realSubject, args);
log.debug("๋ฉ์๋ ํธ์ถ ์ดํ");
return invoke;
}) // ํ๊ฒ์ ์ ๋ณด๊ฐ ํฌํจ๋ Handler
);
// myService.doSomething();
๋ฉ์๋ ํธ์ถ ์ด์
... Do something ...
๋ฉ์๋ ํธ์ถ ์ดํ
myService์ doSomething() ๋ฉ์๋๋ฅผ ์คํํ๊ฒ ๋๋ฉด, ๋ฉ์๋ ํธ์ถ ์ ํ๋ก ๋ก๊ทธ๊ฐ ์ฐํ๋๋ค. ์ฌ๊ธฐ๊น์ง์ ๊ณผ์ ์ ๋ค์ ํ๋ฒ ์ ๋ฆฌํด๋ด ์๋ค.
Client๊ฐ MyService ํ์ ๊ฐ์ฒด์ doSomthing()์ ํธ์ถํฉ๋๋ค. Client๋ ์์ ์ด ํธ์ถํ ํ๊ฒ์ด ํ๋ก์์ธ์ง ์๋์ง ์ ๊ฒฝ ์ธ ํ์ ์์ต๋๋ค.
๋์ ์ผ๋ก ๋ง๋ค์ด์ง ํ๋ก์ ๊ฐ์ฒด๋ Client๊ฐ ์์ฒญํ ๋ฉ์๋์ ์ ๋ณด์ ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๋ฅผ InvocationHandler ๊ตฌํ์ฒด์ invoke() ๋ฉ์๋ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ์ฌ ํธ์ถํฉ๋๋ค.
InvocationHandler์ invoke() ๋ฉ์๋์์ Advice(๋ก๊น )๋ฅผ ์ ์ฉํ์ฌ Target(realSubject)์ ํธ์ถํ ํ, ๋ฐํํฉ๋๋ค.
์ฃผ์ํด์ผ ํ ๊ฒ์ ํด๋น Proxy Bean์ DI ํ ๋, ๋ฐ๋์ ์ธํฐํ์ด์ค ํ์ ์ผ๋ก ์ง์ ํด ์ค์ผ ํฉ๋๋ค. ์๋ํ๋ฉด ํด๋์ค ํ์ ์ผ๋ก DI๋ฅผ ํ๋ ๊ฒฝ์ฐ, UnsatisfiedDependencyException์ด ๋ฐ์ํฉ๋๋ค.
@Controller
public class MyController {
@Autowired
private MyServiceImpl myServiceImpl; // Runtime Error!!
}
@Service
public class MyServiceImpl implements MyService {
@Override
public void doSomething() {
// do something
}
}
๋ค๋ฅธ ๊ฐ์ฒด์งํฅ์ ์ธ ์ด์ ๋ฅผ ์ฐจ์นํ๊ณ ์๋ผ๋, ์ด๋ฌํ ๋ฐํ์ ์๋ฌ๋ฅผ ํผํ๊ธฐ ์ํด์ ์์กด์ฑ ์ฃผ์ ์ ์ธํฐํ์ด์ค ํ์ ์ผ๋ก ์ง์ ํด ์ฃผ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๐กCGLIB (Code Generator Library)
CGLIB์ ํด๋์ค์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํ์ฌ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. org.springframework.cglib.proxy์ Enhancer ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ ํ๋ก์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
@Service
public class MyService {
public void doSomething() {
// do something
}
}
MyService์ doSomething() ๋ฉ์๋์ ๋ํ ํธ์ถ์ ๊ฐ๋ก์ฑ๋ ํ๋ก์ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ด ์๋ค. Enhancer ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฉด Enhancer ํด๋์ค์ setSuperclass() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ MyService ํด๋์ค๋ฅผ ๋์ ์ผ๋ก ํ์ฅํ์ฌ ํ๋ก์๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.debug("๋ฉ์๋ ํธ์ถ ์ด์ ");
Object invoke = methodProxy.invokeSuper(o, objects);
log.debug("๋ฉ์๋ ํธ์ถ ์ดํ");
return invoke;
}
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class); // ํ๊ฒ ํด๋์ค
enhancer.setCallback(new MyMethodInterceptor()); // ํธ๋ค๋ฌ
MyService proxy = (MyService) enhancer.create(); // ํ๋ก์ ์์ฑ
MethodInterceptor ๋ํ ๋ค์๊ณผ ๊ฐ์ด ํจ์ํ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class); // ํ๊ฒ ํด๋์ค
enhancer.setCallback((MethodInterceptor) (Object o, Method method, Object[] objects, MethodProxy methodProxy) -> {
log.debug("๋ฉ์๋ ํธ์ถ ์ด์ ");
Object invoke = methodProxy.invokeSuper(o, objects);
log.debug("๋ฉ์๋ ํธ์ถ ์ดํ");
return invoke;
}); // ํธ๋ค๋ฌ
// myService.doSomething();
๋ฉ์๋ ํธ์ถ ์ด์
... Do something ...
๋ฉ์๋ ํธ์ถ ์ดํ
๋ง์ฐฌ๊ฐ์ง๋ก ๋ฉ์๋ ํธ์ถ ์ ํ๋ก ๋ก๊ทธ๊ฐ ์ฐํ๋๋ค. CGLIB์ ์ด๋ป๊ฒ ํ๋ก์๋ฅผ ์์ฑํ๋์ง ๋ค์ ์ ๋ฆฌํด ๋ด ์๋ค.
Client๊ฐ MyService ํ์ ๊ฐ์ฒด์ doSomthing()์ ํธ์ถํฉ๋๋ค. Client๋ ์์ ์ด ํธ์ถํ ํ๊ฒ์ด ํ๋ก์์ธ์ง ์๋์ง ์ ๊ฒฝ ์ธ ํ์ ์์ต๋๋ค.
enhancer๊ฐ Target ํด๋์ค(MemberService)๋ฅผ ์์ ๋ฐ์ต๋๋ค.
enhancer์์ setCallback()์ ํตํด intercept() ๋ฉ์๋ ๋ด๋ถ์์ Advice ๋ก์ง์ ์ ์ฉํ์ฌ Target(MyService)์ ํธ์ถํ ํ, ๋ฐํํฉ๋๋ค.
๐กJDK Dynamic Proxy vs CGLIB
CGLIB์ ์ต์ด๋ก ํธ์ถ๋์ ๋ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํ๊ณ , ์ดํ ํธ์ถ ์ ์กฐ์๋ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ ์ผ๋ก JDK Dynamic Proxy์ ๋นํด ์ฐ์ํฉ๋๋ค. ์ฐจ์ด๊ฐ ์ด๋์ ๋์ธ์ง ์์ธํ ์๊ณ ์ถ์ผ์๋ค๋ฉด, Baeldung์์๋ ์ฌ์ฉ๋ ๋ฒค์น๋งํฌ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
๐ก์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด๋ CGLIB๋ง ์ฌ์ฉํ๋๋ฐ์?
CGLIB์ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ์ ์ ๊ฐ๊ณ ์์์ต๋๋ค.
CGLIB ์์กด์ฑ์ ์ถ๊ฐํด์ผ ํจ.
default ์์ฑ์๊ฐ ๊ผญ ํ์ํจ.
์์ฑ์๊ฐ ๋ ๋ฒ ํธ์ถ๋จ.
์์กด์ฑ์ ์ถ๊ฐํด์ผ ํ๋ ๋ถ๋ถ์ Spring 3.2๋ถํฐ Spring Core์ CGLIB์ ํฌํจ์์ผฐ์ต๋๋ค. Spring 4.0๋ถํฐ Objensis ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด default ์์ฑ์๊ฐ ํ์ ์๊ณ , ์์ฑ์๊ฐ ๋ ๋ฒ ํธ์ถ๋๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
// spring-boot-autoconfigure์ spring-configuraion-metadata.json
...
{
"name": "spring.aop.proxy-target-class",
"type": "java.lang.Boolean",
"description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).",
"defaultValue": true
},
CGLIB์ด ๊ฐ๊ณ ์๋ ์ฌ๋ฌ ๋ฌธ์ ์ ์ด ํด๊ฒฐ๋์๊ธฐ ๋๋ฌธ์, Spring boot 1.4 ์ด์์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋๋ผ๋ CGLIB์ผ๋ก Proxy๋ฅผ ์์ฑํฉ๋๋ค. ์ฑ๋ฅ์ด ๋ ์ฐ์ํ๋๊น์.
JDK ๋์ ํ๋ก์๋ฅผ ์ฌ์ฉํ์๋ ค๋ฉด @EnableAspectJAutoProxy ์ด๋
ธํ
์ด์
์ proxyTargetClass ์ต์
์ false๋ก ์ฃผ์๊ฑฐ๋, ํ๋กํผํฐ์์ spring.aop.proxy-target-class=false๋ฅผ ์ ์ฉ์์ผ์ฃผ์๋ฉด ๋ฉ๋๋ค.
๐ฏ์ ๋ฆฌ
ํ๊ฒ์ด ํ๋ ์ด์์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ฒฝ์ฐ JDK Dynamic Proxy๋ฅผ, ์๋ ๊ฒฝ์ฐ CGLIB Proxy๋ฅผ ์ฌ์ฉํฉ๋๋ค.
CGlib์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํ์ฌ ํ๋ก์๋ฅผ ์์ฑํด ์ฃผ๊ธฐ ๋๋ฌธ์ JDK Dynamic Proxy๋ณด๋ค ์ฑ๋ฅ์ด ์ข์ต๋๋ค.
Spring Boot์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก CGLIB์ ์ฌ์ฉํฉ๋๋ค.




