김영한의 스프링 핵심 원리(고급편) - AOP의 포인트컷, AOP의 어드바이스 종류

2025. 2. 25. 00:15·김영한의 스프링 핵심 원리 - 고급편

Question

  • AOP에서 어드바이스의 우선순위를 지정하는 방법은?
  • @Around와 @Before 사용의 큰 차이점은?
  • @Around로 모든 기능을 수행할 수 있는데 다른 어노테이션이 있는 이유는?

 

포인트컷을 분리하여 AOP 사용

//Pointcut 분리 하지 않는 방식
@Slf4j
@Aspect
public class AspectV2 {

    @Around("execution(* hello.aop.order..*(..))")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); // 메서드 시그니처를 로깅
        return joinPoint.proceed(); // 실제 메서드를 실행
    }
}


//Pointcut 분리 하는 방식
@Slf4j
@Aspect
public class AspectV2 {

    @Pointcut("execution(* hello.aop.order..*(..))") // 포인트컷 표현식
    private void allOrder() {} // 포인트컷 시그니처

    @Around("allOrder()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("[log] {}", joinPoint.getSignature()); // 메서드 시그니처를 로깅
        return joinPoint.proceed(); // 실제 메서드를 실행
    }
}
  • 이 방식을 사용하면 여러 어드바이저에 포인트컷을 재활용 가능
  • 함수 이름으로 포인트컷 의도를 전달할 수 있어 가독성도 증가
  • 반환 타입은 void, 코드 내용은 비워둬야 함

 

포인트컷 조합

@Slf4j
@Aspect
@Component
public class AspectV2 {

@Around("allOrder() && allService()")
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
            Object result = joinPoint.proceed();
            log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
            return result;
        } catch (Exception e) {
            log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
            throw e;
        } finally {
            log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
        }
    }
}
  • && (AND), || (OR), ! (NOT) 3가지 조합이 가능하다.

 

포인트컷 참조

public class Pointcuts {
    // hello.aop.order 패키지와 하위 패키지에 대한 포인트컷 정의
    @Pointcut("execution(* hello.aop.order..*(..))")
    public void allOrder() {}

    // 타입 패턴이 *Service인 메서드에 대한 포인트컷 정의
    @Pointcut("execution(* *..*Service.*(..))")
    public void allService() {}

    // allOrder와 allService의 조합 포인트컷
    @Pointcut("allOrder() && allService()")
    public void orderAndService() {}
}


public class AspectV3 {

	// 로그 어드바이스 정의
	@Around("hello.aop.order.aop.Pointcuts.allOrder()")
	public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
		log.info("[log] {}", joinPoint.getSignature());
		return joinPoint.proceed();
	}
	...
}
  • 포인트컷을 공용으로 사용하기 위해 별도의 외부 클래스에 모아서 사용도 가능

 

어드바이스 순서

@Slf4j
public class AspectV5Order {

    @Aspect
    @Order(2)
    @Component
    public static class LogAspect {

        @Around("hello.aop.order.aop.Pointcuts.allOrder()")
        public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("[log] {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
    }

    @Aspect
    @Order(1)
    @Component
    public static class TxAspect {

        @Around("hello.aop.order.aop.Pointcuts.orderAndService()")
        public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
            try {
                log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
                Object result = joinPoint.proceed();
                log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
                return result;
            } catch (Exception e) {
                log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
                throw e;
            } finally {
                log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
            }
        }
    }
}
  • 여러 어드바이스가 동시에 적용 되는 케이스에는 일반적으로 순서는 랜덤
  • 이를 위해 @Order 어노테이션을 사용해 우선순위를 줄 수 있는데, 이는 메서드 단위가 아니라 클래스 단위
  • 즉, doLog, doTranscation에 각각 @Order를 붙여줄 수 없고, 각각을 별도의 클래스로 만들어서 @Order를 붙여줘야 의도한대로 동작함

 

어드바이스 종류

  • @Around
    • 메서드 호출 전후에 수행, 가장 강력한 어드바이스, 조인 포인트 실행 여부 선택, 반환 값 변환, 예외 변환 등이 가능
    • ProceedingJoinPoint.proceed()를 호출 해야 다음 대상이 호출되므로, 호출하지 않는 실수를 하면 큰 문제 발생
    • proceed() 를 여러번 실행할 수도 있음(재시도)
  • @Before
    • 조인 포인트 실행 이전에 실행
    • Around는 ProceedingJoinPoint.proceed()를 호출 해야 다음 대상이 호출됨
    • Before는 이를 호출하지 않아도 다음 타겟이 자동으로 호출되며, 이는 실수로 호출하지 않는 문제를 방지 할 수 있음
  • @AfterReturning : 조인 포인트가 정상 완료후 실행
    • @AfterReturning(value = "hello.aop.order.aop.Pointcuts.orderAndService()", returning = "result")
      public void doReturn(JoinPoint joinPoint, Object result)
    • 타겟 함수의 Return 타입과 매개면수 타입이 맞아야 함(부모타입이면 받을 수 있음)
    • returning 속성에 사용된 이름과 파라미터 매개변수 이름이 일치해야 함
    • @Around와 다르게 반환 객체 변경이 불가능하므로 반환하려면 @Around를 사용해야 함
  • @AfterThrowing
    • @AfterThrowing(value = "hello.aop.order.aop.Pointcuts.orderAndService()", throwing = "ex")
      public void doThrowing(JoinPoint joinPoint, Exception ex) {
    • 메서드가 예외를 던지는 경우 실행
    • throwing 속성에 사용된 이름은 어드바이스 메서드의 매개변수 이름과 일치해야 함
    • throwing 절에 지정된 타입과 맞는 예외를 대상으로 실행 (부모 타입을 지정하면 모든 자식 타입은 인정)
  • @After
    • 조인 포인트가 정상 또는 예외에 관계없이 실행(finally)
    • 메서드 실행이 종료되면 실행(finally)
    • 정상 및 예외 반환 조건을 모두 처리
    • 일반적으로 리소스를 해제하는 데 사용
  • @Around 하나로 모든 처리가 가능한데 다른 어드바이스도 있는 이유는?
    • @Around로 jointPoint.proceed()를 호출하지 않는 문제를 @Before로 방지 가능
    • @Before, @After 이런 어노테이션은 의도가 명확하게 들어가 가독성이 좋음

'김영한의 스프링 핵심 원리 - 고급편' 카테고리의 다른 글

김영한의 스프링 핵심 원리(고급편) - AOP 실무 예제  (0) 2025.02.25
김영한의 스프링 핵심 원리(고급편) - 포인트컷 지시자(execution 등)  (1) 2025.02.25
김영한의 스프링 핵심 원리(고급편) - @Aspect, AOP  (0) 2025.02.24
김영한의 스프링 핵심 원리(고급편) - 빈 후처리기  (0) 2025.02.22
김영한의 스프링 핵심 원리(고급편) - 프록시 팩토리  (0) 2025.02.22
'김영한의 스프링 핵심 원리 - 고급편' 카테고리의 다른 글
  • 김영한의 스프링 핵심 원리(고급편) - AOP 실무 예제
  • 김영한의 스프링 핵심 원리(고급편) - 포인트컷 지시자(execution 등)
  • 김영한의 스프링 핵심 원리(고급편) - @Aspect, AOP
  • 김영한의 스프링 핵심 원리(고급편) - 빈 후처리기
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (242)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (118)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      hibernate5module
      빈 후처리기
      단방향 맵핑
      jdk 동적 프록시
      requset scope
      @args
      김영한
      log trace
      gesingleresult
      Thread
      JPQL
      자바
      프록시
      조회 성능 최적화
      양방향 맵핑
      Target
      WAS
      reentarantlock
      락
      @within
      스레드
      typequery
      고급
      @discriminatorvalue
      프록시 팩토리
      cglib
      jpq
      페치 조인
      @discriminatorcolumn
      버퍼
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    김영한의 스프링 핵심 원리(고급편) - AOP의 포인트컷, AOP의 어드바이스 종류
    상단으로

    티스토리툴바