[Spring] @Aspect AOP

전현준·2024년 12월 12일
0

Spring

목록 보기
17/17

개요


인프런 강의 [스프링 핵심 원리 - 고급편] 강의를 기반으로 작성한 글 입니다.



Aspect를 이용하여 프록시 적용

@Aspect는 관점 지향 프로그래밍(AOP)를 가능하게 하는 AspectJ 프로젝트에서 제공하는 애노테이션이다. 스프링은 이것을 차용해서 프록시를 통한 AOP가 가능하게 한다.

자동 프록시 생성기(AnnotationAwareAspectAutoProxyCreator)는 Advisor자동으로 찾아와서 필요한 곳에 프록시를 생성하고 적용해준다.

자동으로 적용해 주는 것 외에도, @Aspect를 찾아서 자동으로 Advisor로 만들어 준다.

@Aspect를 어드바이저로 변환해서 저장하는 과정

  1. 실행 : 스프링 어플리케이션 로딩 시점에 자동 프록시 생성기를 호출한다.
  2. 모든 @Aspect 빈 조회 : 자동 프록시 생성기는 스프링 컨테이너에서 @Aspect 애노테이션이 붙은 스프링 빈을 모두 조회한다.
  3. 어드바이저 생성 : @Aspect 어드바이저 빌더를 통해 @Aspect 애노테이션 정보를 기반으로 어드바이저를 생성한다.
  4. @Aspect 기반 어드바이저 저장 : 생성한 어드바이저를 @Aspect 어드바이저 빌더 내부에 저장한다.

@Aspect 어드바이저 빌더 : @Aspect 정보를 기반으로 Pointcut, Advice, Advisor를 생성하고 보관하는 것을 담당. @Aspect의 정보를 기반으로 어드바이저를 만들고, `@Aspect 어드바이저 빌더 내부 저장소에 캐시함. 이미 만들어져 있는 경우 캐시에 저장된 어드바이저를 반환

@Aspect
public class LogTraceAspect {
    private final LogTrace logTrace;

    public LogTraceAspect(LogTrace logTrace) {
        this.logTrace = logTrace;
    }

    @Around("execution(* hello.proxy.app..*(..))") // pointcut
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
        // advice 로직
        TraceStatus status = null;
        try{
            String message = joinPoint.getSignature().toShortString();
            status = logTrace.begin(message);

            // 로직 호출
            Object result = joinPoint.proceed();

            logTrace.end(status);
            return result;
        } catch (Exception e){
            logTrace.exception(status, e);
            throw e;
        }
    }
}

프록시에 중요한 것은 PointcutAdvice이다.

Aspect 애노테이션을 통해서 AspectJ를 적용하자.

그리고 Pointcutexecute 메소드 상단에 @Around 애노테이션으로 사용이 가능하다.

포인트 컷을 자세히 보면 * hello.proxy.app..*(..))인데, hello.proxy.app 패키지 안에 있는 모든 클래스와 메소드에 적용된다.

AOP

기존 문제점

모든 프로그램에는 핵심 기능과 부가 기능이 있다.

클래스도 핵심 기능부가 기능으로 나눌 수 있다.

주문 로직 클래스

  • 핵심 기능 : 주문 하는 로직 (주문 → DB 저장..)
  • 부가 기능 : 로그 출력하기

이 부가 기능인 로그 출력하기 메소드가 여러 클래스에서 필요할 수 있다.

Controller - Service - Repository를 예시로 보면, 이 모든 클래스들에서 동일한 메소드가 필요할 수 있다.

이것을 횡단 관심사 라고 한다.

근데 동일한 메소드를 모든 클래스에 생성하게 되면 불필요한 코드 생성과 수정에 문제가 생긴다.

문제들

  • 부가 기능을 적용할 때 아주 많은 반복이 필요하다.
  • 부가 기능이 여러 곳에 퍼져서 중복 코드를 만들어낸다.
  • 부가 기능을 변경할 때 중복 때문에 많은 수정이 필요하다.
  • 부가 기능의 적용 대상을 변경할 때 많은 수정이 필요하다.

AOP Aspect

부가 기능을 핵심 기능에서 분리하고 한곳에서 관리.

부가 기능을 어디에 적용할지 선택하는 기능도 만들었다.

Aspect를 통해서 하나의 모듈로 만들었다.

AOP Asepct-Oriented Programming : Aspect를 사용한 프로그래밍 방식을 관점 지향 프로그래밍
AspectJ : Aspect를 지원하기 위한 프레임워크


AOP 방법

AOP를 사용해서 부가 기능 로직을 추가하는 방법
1. 컴파일 시점
2. 클래스 로딩 시점
3. 런타임 시점 (프록시)

컴파일 시점

.java 소스 코드를 컴파일러를 사용해서 .class로 만드는 시점에 부가 기능 로직을 추가할 수 있다.

AspectJ 컴파일러가 Aspect를 확인해서 해당 클래스가 적용 대상인지 확인하고, 적용 대상인 경우에 부가 기능 로직을 적용함.

*Weaving이라고도 한다


클래스 로딩 시점

자바를 실행하면 자바 언어는 '.class' 파일을 JVM 내부의 클래스 로더에 보관한다.

이때 중간에서 '.class'파일을 조작한 다음 JVM에 올릴 수 있다. Java Imstrumentation을 통해서 가능

단점
자바를 실행할 때 특별한 옵션 (java -javaagent)을 통해 클래스 로더 조작기를 지정해야한다.
굉장히 번거롭고 운영하기 어려움.


런타임 시점

컴파일도 끝나고, 클래스 로더에 다 올라가서 자바가 실행되고 난 뒤를 런타임 시점이라고 한다.

이때는, 메소드를 수정할 수 없기 때문에 스프링 컨테이너의 도움을 받아서 프록시와 DI, 빈 포스트 프로세서를 사용해서 적용해야한다.

이것이 프로시 방식의 AOP이다.

AOP 적용 위치

  • 적용 가능 지점 (조인 포인트) : 생성자, 필드 값 접근, static 메소드 접근, 메소드 실행
    → 컴파일 시점과 클래스 로딩 시점에 적용하는 AOP는 어디든 가능함
  • 프록시 방식을 사용하는 스프링 AOP는 메소드 실행 지점에만 AOP 적용 가능
스프링 AOPAspectJ AOP
프록시 방식만 사용 가능컴파일 시점, 클래스 로딩 시점, 프록시 방식
단순하게 사용가능굉장히 복잡함
스프링 어플리케이션특별한 컴파일러, AspectJ 문법, 자바 실행 옵션
profile
백엔드 개발자 전현준입니다.

0개의 댓글

관련 채용 정보