Backend/Spring

Spring AOP의 구동 원리와 Proxy와의 관계

Juwon2106 2022. 1. 13. 11:20
728x90

지난 포스팅에서는 Filter, Interceptor, AOP 간의 차이점을 알아보았습니다.

 

이번 포스팅은 JVM 런타임 동안 Spring이 무엇을 하는지 ,

 

어떤 동작을 하고 무슨 준비를 하는지,

 

Weaving과 Proxy의 역할, SOLID의 원칙 중 어떤 것이 적용되는지 알아보겠습니다.

 

첫째로 Weaving이 무엇인지 알아보겠습니다.

 

AOP

Weaving 

Advice를 핵심 로직에 적용하는것을 의미하며 3가지 방식이 존재합니다.

 

Compile-time Weaving

Load-time에 대한 절차가 없어서 성능 하락 없이 구성이 가능합니다.

Lombok과 같은 Compile Weaving 시 간섭하는 Plugin들과 충돌이 발생합니다.

 

Class Load-time Weaving

Application Context에 Load 된 객체들을 불러온 다음 AspectJ Weaver에 의해 객체들을 Weaving 합니다.

객체들을 모두 Load한 다음 Weaving을 하기 때문에 약간의 성능 하락이 발생합니다.

 

Run-time Weaving

Spring AOP에서 사용하는 방식으로 Source Code나 실제 클래스 정보 자체를 변경하지 않고

도중에 Proxy 객체로 AOP를 적용합니다.

 

그렇다면 Proxy도 무슨 역할을 하는지 알아보겠습니다.

 

Proxy

Proxy는 SOLID의 5가지 원칙 중 O 를 적용하고 있습니다.

 

OCP ( OCP : Open-Close Principal 개방 폐쇄의 원칙 )
개방-폐쇄 원칙 OCP는 '소프트웨어 객체( Class, Module, Function 등 )는 확장에 대해 열려 있어야 하고,
수정에 대해서는 닫혀 있어야 한다'

 

Proxy는 앞선 포스팅에서 말한 대리 객체 혹은 가짜 객체로, 실행할 Target 객체를 감싸 

 

Target의 요청을 대신 받아주는 랩핑( Wrapping ) Object입니다.

 

Client에서 Target 객체를 호출하게 되면 Target이 아닌 Target을 랩핑하고 있는 Proxy가 호출되어 

 

Target 메서드 실행전에 선처리, Target 메소드 실행 후, 후처리를 실행하도록 설계되어있습니다.

 

다시 Spring 구동으로 돌아가면

 

Spring이 Run-time Weaving시에 Weaving을 통해서 Proxy Object를 생성합니다.

 

AOP에서 Spring Bean에 Proxy 주입

Bean 후처리기 중에서 자동으로 Proxy를 생성하기 위해 DefaultAdvisorAutoProxyCreator라는 Class를 사용합니다.

 

이 Class는 Adviser를 이용한 자동 프락시 생성기입니다. Bean Object에 일부를 Proxy로 Wrapping 하고, Proxy를 Bean 대신 등록시킬 수 있습니다.

 

DefaultAdvisorAutoProxyCreator가 Bean 후처리로 등록되어있다면, Spring은 Bean 객체를 만들 때마다 후처리기에게 Bean을 보냅니다.

 

Bean 후처리 기를 이용한 Proxy 자동생성

  1. 후처리 기는 Bean으로 등록된 모든 Adviser 내의 Pointcut을 이용해 전달받은 Bean이 Proxy를 적용할 대상인지    확인합니다.
  2. Proxy 적용 대상이면 내장된 ProxyCreator를 통해 현재 Bean에 대한 Proxy를 생성하고 Adviser를 연결합니다.
  3. Proxy가 생성되면 전달받은 Target Bean 객체 대신에 Proxy 객체를 Spring Container에게 돌려줍니다.
  4. Container는 Bean 후처리기가 돌려준 Proxy Object를 Bean으로 등록합니다.

 

이러한 후처리 기를 통해 매번 ProxyFactoryBean을 Bean으로 등록하지 않아도 많은 Target Object에 자동으로 Proxy를 Wrapping 할 수 있습니다.

 

Proxy 객체 생성 방식( Run-time weaving )의 첫 번째로는, 

 

JDK Dynamic Proxy

 

JDK Dynamic Proxy 방식이 존재하며 Target 객체가 Interface를 구현( Implement )하는 클래스라면 

Interface를 기반으로 Proxy 객체를 생성하기 때문에 Interface에 정의되지 않는 Method에 대해서는

AOP가 적용되지 않는 단점이 있습니다.

 

Proxy 객체 생성 방식( Run-time weaving )의 두 번째로

CGLIB

CGLIB ( Code Generator Library )는 즉 코드 생성 라이브러리로 Run-time시 동적으로 Java Class의 Proxy를 생성하는

기능을 제공합니다.

 

CGLIB는 Target 객체가 Interface를 Implement하지 않고 바로 Class를 사용한다면, Spring은 CGLIB를 이용하여 Class에 대한 Proxy 객체를 생성합니다.

 

CGLIB는 대상 Class를 Extend( 상속 )로 구현합니다.

 

따라서 Class가 final인 경우에는 Proxy를 생성할 수 없습니다.

 

CGLIB는  JDK Dynamic Proxy보다 상대적으로 성능이 빠른 장점을 갖고 있습니다.

 

출처:  https://skasha.tistory.com/45  [카샤의 만개시기]

Spring Boot Project의 리더가 언급한 말을 인용하면

We've generally found cglib proxies less likely to cause unexpected cast exceptions.
일반적으로 CGLIB가 예외를 발생시킬 가능성이 낮다는 걸 발견하였다고 합니다.

이로 인해 Spring Boot는 AOP의 기본 Strategy를 CGLIB로 적용( 변경 )하였습니다.

 

여기서 한 가지 의문점을 가질 필요가 있습니다.

 

굳이 왜 실제 객체에 접근하지 않고 복잡한 Wrapping Class인 Proxy 객체를 사용할까?

 

결론적으로 AOP도 Bean이기 때문입니다. 

 

Object의 역할로서만 존재하기 때문에 해당 클래스에는 Aspect와 JoinPoint 두 가지로만 함수로 정의되어 있으며

Aspect와 Target을 연결해주는 역할이 없기 때문입니다.

 

그래서 연결해주는 역할을 Proxy 객체가 대신해주게 되는 것입니다.

 

 

 

Proxy Flow

위 그림의 Proxy의 흐름을 보면

@Transaction
public void helloJuwon(){
	int cnt = greetingService.cntAreadyHello( new Date() );
    if( cnt > 0){
    	return;
    }
    greeringService.insertHello("Y", new Date() );
}

 

작성한 @Transaction 메서드를 호출하면 Proxy가 먼저 호출되어 Wrapping을 하고 그 다음 Transaction 코드,

다음은 내가 호출한 메소드를 실행합니다.

 

이번 포스팅으로 Spring AOP의 구동원리, Proxy의 역할 사용하는 이유에 대해 알아보았습니다.

 

두가지 Weaving에 대한 참고하면 도움되는 포스팅 

https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html

728x90