Spring AOP Weaving, Proxy

Kereu_·2022년 8월 7일
1

Weaving?

  • Weaving은 Pointcut에 의해서 결정된 타겟의 Join Point에 부가기능(Advice)를 삽입하는 과정을 뜻한다.
  • Weaving은 AOP가 핵심기능(타겟)의 코드에 영향을 주지 않으면서 필요한 부가기능(어드바이스)를 추가할 수 있도록 해주는 핵심적인 처리과정이다.

Pointcut - 어드바이스를 적용할 타겟의 메서드를 선별하는 정규표현식이다
Advice - 어드바이스는 타겟에 제공할 부가기능을 담고 있는 모듈이다.
Join Point - 어드바이스가 적용될 수 있는 위치를 말한다.

종류

Compile Time Weaving(CTW)

  • AspectJ에는 AJC (AspectJ Compiler)라는 컴파일러가 있는데 Java Compiler를 확장한 형태의 컴파일러이다.
  • AJC를 통해 java파일을 컴파일 하며, 컴파일 과정에서 바이트 코드 조작을 통해 Advisor 코드를 직접 삽입하여 위빙을 수행한다.
  • 장점으로는 3가지 위빙 중에서는 가장 빠른 퍼포먼스를 보여준다.
  • 하지만 컴파일 과정에서 lombok과 같이 컴파일 과정에서 코드를 조작하는 플러그인과 충돌이 발생할 가능성이 아주 높다. (거의 같이 사용 불가)
  • AspectJ에는 AJC (AspectJ Compiler)라는 컴파일러가 있다.

Runtime Weaving(RTW)

  • Spring AOP에서 사용하는 방식으로, Proxy를 생성해 실제 타깃 오브젝트의 변형없이 위빙을 수행한다.
  • 실제 런타임시 Method 호출 시에 위빙이 이루어지는 방식이다.
  • 소스파일, 클래스 파일에 변형이 없다는 장점이 있지만, Point Cut에 대한 Advice 수가 늘어날수록 성능이 떨어진다는 단점이 있다.

Load Time Weaving(LTW)

  • ClassLoader를 이용해 클래스가 JVM에 로드될 때 바이트 코드 조작을 통해 위빙되는 방식으로 컴파일 시간은 상대적으로 CTW보다 짧다. 하지만 오브젝트가 메모리에 올라가는 과정에서 위빙이 일어나기때문에 런타임시 시간은 CTW보다 상대적으로 느리다.

  • Application Context에 객체가 로드될 때, 객체 핸들링이 발생하므로 퍼포먼스가 저하된다.

Post-Compile Weaving

  • 컴파일 후 기존 클래스 파일과 JAR 파일을 위빙하는 데 사용, Compile Time Weaving 같은 방식으로 처리한다.

Spring AOP와 AspectJ

  • AspectJ가 Compile 시점과 Load 시점 Weaving을 사용
  • Spring AOP는 RunTime Weaving을 사용한다.
  • Spring AOP보다 AspectJ가 속도와 기능적인 측면에 조금 좋은 성능을 보이지만 바이트 코드 조작을 위해 JVM의 실행 옵션을 변경하거나, 별도의 바이트코드 컴파일러를 사용 등에 번거로운 작업이 따르므로 Spring AOP 사용 후 그 이상의 기능이 필요한 경우 AspectJ를 확장해서 사용하는 것을 추천한다.

Proxy

Spring AOP는 Proxy의 메커니즘을 기반으로 AOP Proxy를 제공하고 있다.
Spring AOP는 사용자의 특정 호출 시점에 IoC 컨테이너에 의해 AOP를 할 수 있는 Proxy Bean을 생성하며 동적으로 생성된 Proxy Bean은 타깃의 메소드가 호출되는 시점에 부가기능을 추가할 메소드를 자체적으로 판단하고 가로채어 부가기능을 주입한다.

Spring에선 런타임 위빙을 할 수 있도록 상황에 따라 JDK Dynamic Proxy와 CGLIB 방식을 통해 Proxy Bean을 생성을 해주는데요. 그렇다면 이 두 가지 AOP Proxy는 어떠한 상황에 생성하게 되는걸까?

종류

Spring은 AOP Proxy를 생성하는 과정에서 자체 검증 로직을 통해 타깃의 인터페이스 유무를 판단한다.
인터페이스가 있을 경우 JDK Dynamic Proxy,
인터페이스가 없을 경우 CGLib 방식을 통해 프록시를 생성한다.

JDK Dynamic Proxy

  • JDK Dynamic Proxy란 Java의 리플렉션 패키지에 존재하는 Proxy라는 클래스를 통해 생성된 Proxy 객체를 의미
  • 타깃의 인터페이스를 기준으로 Proxy를 생성해준다.
  • 개발자가 타깃에 대한 정보를 잘 못 주입할 경우를 대비하여 JDK Dynamic Proxy는 내부적으로 주입된 타깃에 대한 검증 코드를 형성하고 있다.

CGLib(Code Generator Library)

  • Code Generator Library의 약자로, 클래스의 바이트코드를 조작하여 Proxy 객체를 생성해주는 라이브러리
  • CGLib을 사용하여 인터페이스가 아닌 타깃의 클래스에 대해서도 Proxy를 생성하며 이때 Enhancer라는 클래스를 통해 Proxy를 생성한다.
  • CGLib은 제공받은 타깃 클래스에 대한 바이트 코드를 조작하여 Proxy를 생성, 즉 해당 타깃에 대한 정보를 받기 때문에 성능적으로 JDK Dynamic Proxy보다 좋다.

사용

Spring은 성능이 좋은 CGLib아닌, JDK Dynamic Proxy를 기반으로 Proxy를 생성해주고 있습니다.
그 이유는 GCLib 3가지 단점이 한계점이 있기 때문이다.

  • net.sf.cglib.proxy.Enhancer 의존성 추가
  • default 생성자
  • 타깃의 생성자 두 번 호출

spring은 Enhancer 클래스를 기본적으로 지원하지 않으므로 별도로 의존성을 추가해야 하며, CGLib 생성 위해 파라미터가 없는 default 생성자가 필요하며, 호출을 받아 proxy 생성 시 타깃에 생성자를 두번 호출한다는 단점이 있다.

Spring Boot CGLib 선택

어느 시점부터 Spring Boot에선 CGLib을 방식으로 Proxy를 생성한다.
그 이유는 다음과 같다.

  • Spring 3.2 버전부터 CGLib을 Spring Core 패키지에 포함
  • 4 버전에선 Objensis 라이브러리의 도움을 받아 default 생성자 없이도 Proxy를 생성 가능과 생성자가 2번 호출되던 상황도 같이 개선

즉 CGLib이 가지고 있던 대부분의 한계들이 개선이 되어, Spring에선 성능이 좋은 CGLib으로 Proxy를 생성하게 되었습니다.

그러면 이제 CGLib만 알고 있으면 되냐라는 질문이 나올 수 있다.
그 대답은 NO이다.
JDK Dynamic Proxy는 Spring AOP의 AOP 기술의 근간이 되는 방식이기 때문에 Spring에서 사용되는 AOP의 기술들은 Proxy 메커니즘을 따르고 있습니다.
즉 CGLib이든 JDK Dynamic Proxy든 Proxy 메커니즘을 따른다는 점을 인지해야한다.

참조

profile
Issue_Save

0개의 댓글