HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
✍🏻
Learnary (learn - diary)
/
[Spring AOP] - Proxy

[Spring AOP] - Proxy

progress
Done
Tags
Spring
Build UpWhatWhy REFER

Build Up


  • 프록시는 ‘대리’ 라는 의미로 사용되어 진다.
  • 그리고 프록시 패턴이라는 코드적 명세도 존재하며 해당 패턴은 무거운 작업을 수행하는 실제 구현체를 호출 하기 직전까지 delay시키는 방식으로 진짜 호출되어야 하는 시점에 사용할 수 있게 해준다.
 

What


Spring AOP는 JDK Proxy(Dynamic Proxy)와 CGLib 방식으로 AOP를 제공한다.
 
notion image
Spring AOP는 특정 호출 시점에 IoC 컨테이너에 의해 AOP를 할 수 있는 Proxy Bean을 생성한다.
동적으로 생성된 Proxy 빈은 Target 메소드가 호출되는 시점에 부가기능을 추가할 메소드를 자체적으로 판단하고 가로채어 부가기능을 주입해준다.
이처럼 호출 시점에 동적으로 위빙(프록시를 만드는 행위)를 런타임 위빙이라고 한다.
 
Spring은 런타임 위빙을 할 수 있도록 상황에 따라 JDK Dynamic 또는 CGLib 방식을 통해 Proxy Bean을 생성한다.
 
인터페이스 유무에 의한 프록시 동작 방식
notion image
Spring은 AOP Proxy를 생성하는 과정에서 자체 검증 로직을 통해 Target의 유무를 판단한다.
이때 만약 Target이 하나이상의 인터페이스를 구현하고 있는 클래스라면 JDK 방식 없으면 CGLib 프록시 패턴을 사용한다.
 
차이점
notion image
 
JDK Proxy (자바 기본 제공 라이브러리)
AOP를 적용하여 구현된 클래스의 인터페이스를 프록시 객체로 구현해서 코드를 끼워 넣는 방식이다.
인터페이스를 구현하는 단점이 있다.
예전 ServiceImpl을 구현한 이유도 이러한 프록시를 이용하기 위해 그랬다.
public class ExamDynamicHandler implements InvocationHandler { private ExamInterface target; // 타깃 객체에 대한 클래스를 직접 참조하는것이 아닌 Interface를 이용 public ExamDynamicHandler(ExamInterface target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub // 메소드에 대한 명세, 파라미터등을 가져오는 과정에서 Reflection 사용 String ret = (String)method.invoke(target, args); //타입 Safe하지 않는 단점이 있다. return ret.toUpperCase(); //메소드 기능에 대한 확장 } }
Dynamic Proxy는 InvocationHandler 라는 인터페이스를 구현한다.
invoke 메소드를 오버라이딩하여 Proxy의 위임 기능을 수행하는데, 이때 메소드에 대한 명세와 파리미터를 가져오는 과정에서 리플렉션을 사용한다.
 
CGLib Proxy
외부 써드 파티 라이브러리이며 리플렉션을 사용하지 않고 바이트코드 조작을 통해 프록시 객체를 생성한다.
또한 인터페이스를 불필요하게 작성할 필요가 없으며 구현체를 상속받는 것으로 문제를 해결하기 떄문에 성능상 이점이 있다.
spring3.2 버전부터 의존성을 추가할 필요가 없어짐.
 
상속을 하여 프록시를 생성하기 때문에 Final 메소드 클래스에 대해 재정의 할 수 없으므로 프록시를 생성하지 못하는 단점이 있지만 리플랙션이 아닌 바이트 코드 조작으로 성능상 이점이 있다.
 
// 1. Enhancer 객체를 생성 Enhancer enhancer = new Enhancer(); // 2. setSuperclass() 메소드에 프록시할 클래스 지정 enhancer.setSuperclass(BoardServiceImpl.class); // Target 클래스 enhancer.setCallback(NoOp.INSTANCE); // 3. enhancer.create()로 프록시 생성 Object obj = enhancer.create(); // 4. 프록시를 통해서 간접 접근 BoardServiceImpl boardService = (BoardServiceImpl)obj; boardService.writePost(postDTO);
 
CGLib는 Enhancer라는 클래스를 바탕으로 Proxy를 생성한다.
enhancer.setCallback(NoOp.INSTANCE); 해당 부분은 Enhancer 프록시 객체가 직접 원본 객체에 접근하기 위한 옵션으로 사용되고 원본 객체를 직접 호출하기 보다는 CallBack를 사용하는 것을 알 수 있다.
 
CGLib에서 가장 많이 사용하는 콜백은 net,sf.cglib.proxy.MethodInterceptor인데, 프록시와 원본 객체 사이에 인터셉터를 두어 메소드 호출을 조작하는 것을 도와줄 수 있게 된다.
 
SpringBoot는 기본적으로 CGLib 프록시를 사용하여 해당이점을 누리고 있다.

Why


Spring은 프록시 패턴을 이용하여 바이트 코드를 조작 또는 코드를 직접 끼워 넣어 부가 기능을 수행할 수 있게 제공하고 있다.
관심사를 분리하고 핵심적인 비즈니스 로직만을 담기 위한 전략으로 Spring은 AOP라는 기능을 제공한다.
AOP의 동작원리를 이해하고 해당 기능을 사용하기 위해서 필요하기 때문이다.

 REFER


[Spring] AOP (Aspect-Oriented-Programming) - SW Developer
1. AOP(관점 지향 프로그래밍)
https://wonyong-jang.github.io/spring/2020/06/02/Spring-AOP.html