티스토리 뷰

스프링, 자바

AspectJ 과 Spring AOP

killog 2021. 3. 29. 19:22
반응형

Intro

이번에 @LoginRequired 를 구현하면서 계속된 에러의 원인(로그에도 안찍히고 구동이 안됨)은 .aj 확장자를 이용해 만들어서 스프링 빈으로 등록되지 않았기 때문이였다.

생성자를 만들어 로그를 찍어보고 나서야 깨달았다.

그렇다면 요 미운 .aj 는 어디에 쓰이는걸까? 한번 탐구해보자.

이번 글의 한줄 요약. 스프링 AOP 는 간단 스프링용 AOP 이고, 완전체는 AspectJ 이다.(확장자 .aj assertj 컴파일러 사용). 스프링용 AOP 는 등록된 빈의 메소드 실행에만 사용이 가능하다.

여담이지만, 스프링 AOP 만들때, AspectJ 만든 사람이 CTO 로 있었다고 한다. 큰틀은 비슷하지 않을까 추정한다.


AOP 개념

  • Aspect : 어플리케이션의 여러위치에흩어져있고, 일반적으로 실제 비즈니스 로직(예. 트랜잭션 관리, 캐싱)과 다른 표준 코드, 기능이다. 각 Aspect는 Cross-Cutting 기능에 중점을 둔다. (여러 객체에 공통 적용되는 부가 기능의 관심사항)(cf. 핵심로직)
  • Joinpoint: 프로그램 실행 중 특정 지점이다. (예. 메서드 실행, 생성자 호출, 또는 필드 할당 )
  • Advice: 특정 Joinpoint에서 Aspect가 취한 조치
  • Pointcut: Joinpoint과일치하는 정규식,JoinpointPointcut과 일치할때마다 해당Pointcut과 관련된 지정된 Advice가 실행된다.
  • Weaving: Advice된 객체를 생성하기 위해 대상 객체와 Aspect을 연결하는 프로세스이다.

기능과 목표가 다르다.

간단히 말해, Spring AOP와 AspectJ는 다른 목표를 가지고 있다.

Spring AOP는 프로그래머가 직면하는 가장 일반적인 문제를 해결하기 위해 Spring IoC에서 간단한 AOP 구현을 제공하는 것을 목표로합니다. 완전한 AOP 솔루션 이 아니라 Spring 컨테이너가 관리하는 Bean에만 적용 할 수 있다.

반면 AspectJ는 완전한 AOP 솔루션을 제공하는 것을 목표로하는 독창적 인 AOP 기술이다. 더 강력하지만 Spring AOP보다 훨씬 더 복잡하다. AspectJ가 모든 도메인 객체에 적용될 수 있다는 점도 주목할 가치가 있다.


Weaving

AspectJ와 Spring AOP는 성능 및 사용 편의성과 관련하여 동작에 영향을 미치는 다른 유형의 Weaving을 사용한다.

Aspectj weaving

AspectJ는 세 가지 유형의 weaving를 사용한다.

  1. Compile-time weaving: AspectJ 컴파일러는 우리의 aspect와 우리 애플리케이션의 소스 코드를 입력으로 취하고 출력으로 엮인 클래스 파일을 생성한다.
  2. Post-compile weaving (컴파일 후 위빙): binary weaving.이라고도한다. 기존 클래스 파일과 JAR 파일을 위빙하는데 사용한다.
  3. Load-time weaving : 클래스 로더가 클래스 파일을 JVM에로드 할 때까지 weaving이 연기된다는 점이 다르다.

더 자세한건 Intro to AspectJ 이걸 보자.

즉, 런타임때 아무것도 안한다. Aspect를 코드에 Weaving하기 위해, AspectJ compiler(ajc)라는 컴파일러를 도입한다. 이 컴파일러를 통해 프로그램을 컴파일하고 작은 런타임 라이브러리를 추가해 동작시킨다.

Spring AOP weaving

Spring AOP 의 경우 Runtime weaving을 사용한다. 프록시 패턴을 기반으로 한다.

즉, 대상 객체에 Aspect 를 적용하기 위해 대상 객체의 Proxy를 생성한다.

  • Runtime weaving: Aspect 가 대상 객체의 Proxy(JDK 동적 Proxy 나 CGLIB 의 Proxy)를 실행시 Weaving 된다.

Spring AOP는 사용자의 특정 호출 시점에 IoC 컨테이너에 의해 AOP를 할 수 있는 Proxy Bean을 생성해준다. 동적으로 생성된 Proxy Bean은 타깃의 메소드가 호출되는 시점에 부가기능을 추가할 메소드를 자체적으로 판단하고 가로채어 부가기능을 주입해준다. 이처럼 호출 시점에 동적으로 위빙을 한다 하여 런타임 위빙(Runtime Weaving)이라 한다. (인용)

JDK Dynamic Proxy vs CGLIB(Code Generator Library)

( JDK 동적 Proxy 나 CGLIB 의 차이 ) 이 블로그에서 자세히 확인할 수 있다.

  • JDK Dynamic Proxy와 CGLIB 의 차이는 간단 정리하자면, JDK 는 인터페이스 기반, CGLIB 는 클래스 기반(클래스의 바이트코드조작)으로 프록시를 생성한다.
  • Spring AOP에서는 이때 만약 타깃이 하나 이상의 인터페이스를 구현하고 있는 클래스라면JDK Dynamic Proxy의 방식으로 생성되고 인터페이스를 구현하지 않은 클래스라면CGLIB의 방식으로 AOP 프록시를 생성한다.

https://www.baeldung.com/spring-aop-vs-aspectj

  • CGLib은 타깃에 대한 정보를 제공 받기때문에 성능이 더 좋다.
  • CGLib 의 한계점
    • default 생성자 필요 (-> Objensis 라이브러리의 도움을 받아 default 생성자 없이도 Proxy를 생성할 수 있게 됨 )
    • 생성자 2번 호출 (-> Objensis 라이브러리의 도움을 받아 생성자 2번 호출 없이도 Proxy를 생성할 수 있게 됨 )
    • net.sf.cglib.Enhancer 의존성 주입 (->CGLib을 Spring Core 패키지에 포함함 )
  • Spring은 JDK Dynamic Proxy만 지원했지만, SpringBoot에서는 CGLIB의 3가지 한계를 보완해 인터페이스던 그렇지 않던 CGLIB를 제공한다.

프록시 메커니즘 공식문서


Join Point

Spring AOP가 프록시 패턴을 기반으로한다. 이 때문에 대상 Java 클래스를 하위 클래스로 분류하고 그에 따라 Cross-Cutting Concerns 를 적용해야합니다.

그러나 한계가 있다. final 클래스에는 재정의 할 수 없기 때문에 Cross-Cutting ConcernsAspect를 적용 할 수 없다. 런타임 예외가 한다.

이 한계는staticfinal메서드에도 동일하다. Spring Aspect는 그 메서드들이 오버라이드될 수 없기 때문에 적용될 수 없다. 이러한 한계 때문에 Spring AOP는 메서드 실행에만 Joinpoint를 지원한다.

하지만, AspectJ는 런타임 이전에 실제 코드에 Cross-Cutting ConcernsWeaving하기 때문에 더 많은 JoinPoint 를 지원한다.

JoinpointSpring AOPAspectJ
메서드 호출XO
메서드 실행OO
생성자 호출XO
생성자 실행XO
Static 초기화 실행XO
개체 초기화XO
필드 참조XO
필드 할당XO
핸들러 실행XO
Advice 실행XO

간편성

AspectJ를 사용하기 위해선, AspectJ compiler(ajc)라는 컴파일러를 도입해야하고 모든 라이브러리들을 재 패키징해야하기 때문에, 스프링 AOP 가 좀 더 간편하다.


성능

성능면에서, 컴파일 시점 Weaving은 런타임 Weaving에 비해 훨씬 빠르다. SSpring AOP는 프록시 기반 프레임 워크이므로 애플리케이션 시작시 프록시가 생성됩니다. 또한 측면 당 메서드 호출이 몇 개 더있어 성능에 부정적인 영향을준다.

반면에, AspectJ는 Aspect를 애플리케이션이 실행되기 전에 Weaving 하기 때문에 Spring AOP와는 달리 런타임 오버헤드가 없다.

이러한 이유에서 이 밴치마킹은 AspectJ가 Spring AOP보다 8배에서 35배 가까이 빠른것을 증명해준다.


결론

Spring AOPAspectJ
순수 자바로 구현Java 프로그래밍 언어의 확장을 사용하여 구현 됨
별도의 컴파일 과정이 필요 없음LTW가 설정되지 않은 경우 AspectJ 컴파일러 (ajc)가 필요
런타임 위빙 만 사용할 수 있다런타임 위빙을 사용할 수 없다. 컴파일 타임, 사후 컴파일 및로드 타임 Weaving 지원
덜 강력 함 – 메서드 수준 위빙 만 지원더욱 강력 함 – 필드, 메서드, 생성자, 정적 이니셜 라이저, 최종 클래스 / 메서드 등을 엮을 수 있다.
Spring 컨테이너가 관리하는 Bean에서만 구현 가능모든 도메인 개체에서 구현 가능
메서드 실행 포인트 컷 만 지원모든 포인트 컷 지원
프록시는 대상 개체로 만들어지며 이러한 프록시에 측면이 적용애플리케이션이 실행되기 전에 (런타임 전에) Aspect가 코드로 직접 짜여짐
AspectJ보다 훨씬 느림더 나은 성능
배우고 적용하기 쉬움Spring AOP보다 비교적 복잡함

내 프로젝트는 어떻게 되었나?

@LoginRequired 를 성공적으로 구현했다. 정상 작동확인하는 것을 확인했다.


참고 문헌

https://logical-code.tistory.com/118
https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html
https://www.baeldung.com/spring-aop-vs-aspectj
http://wonwoo.ml/index.php/post/1708
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying

반응형

'스프링, 자바' 카테고리의 다른 글

Optional  (0) 2021.04.09
자바의 Stream API  (0) 2021.04.09
Log4j 레벨 간단 정리  (0) 2021.03.29
Dispatcher-Servlet  (0) 2021.03.04
Lombok 도큐먼트 정리  (0) 2021.03.04
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함