AOP - Pointcut 작성

박준서·2024년 10월 17일
4

Web

목록 보기
11/16
post-thumbnail

Pointcut

  • advice를 적용할 target의 메서드를 결정하는 메서드 선정 알고리즘
  • @Before 등 애너테이션의 value 값이다.
  • pointcut을 작성할 때는 execution, within, bean 등 지정자와 함께 사용한다.
execution메서드의 signature 기반으로 한 정교한 포인트 컷 작성법. 가장 많이 사용@Before(”execution(String *..TBean.getName())”)
within빈 클래스 기반으로 작성@Before(”within(com..TBeanImpl)”)
bean빈의 이름 기반으로 작성@Before(”bean(pointcutTBean)”)

execution을 이용한 메서드 패턴 지정

execution에 작성하는 메서드의 패턴은 위 처럼 return_type, class, method_name, parameter로 나뉜다.

각 부분에 사용되는 기호 설명

  • return_type
    • 메서드의 리턴 타입을 나타내며 필수 입력사항
    • 기호
      • *: 타입에 무관
      • ! : 타입에 대한 부정 (ex. !String, !void)
  • package + class
    • 패키지를 포함하는 클래스이름으로 메서드가 선언된 클래스 또는 상위타입
    • 선택사항으로 생략 가능하며, 생략하지 않을 경우 메서드 이름과 .로 연결
    • 기호
      • .. : 0개 이상의 하위 패키지 대체. 맨 처음에는 등장 할 수 없음
      • *: 0개이상의 문자열 대체
  • method_name
    • 메서드의 이름을 나타내며 필수 입력 사항
    • 기호
      • *: 0개 이상의 문자열을 대체
  • paramter
    • 메서드의 파라미터를 이름 없이 타입만 입력하며 선택사항
    • 기호
      • *: 파라미터의 타입에 무관하게 1개의 파라미터 대체
      • .. : 파라미터의 타입에 무관하게 0개 이상의 파라미터 대체

참고 사항

  • return_type이나 parameter 객체형을 사용할 때는 패키지 이름까지 명시해주는 것이 좋다. pointcut에 클래스 이름만 썼을 때는 중복되는 클래스를 구분하지 못하기 때문이다. 예를 들어, List 라고 썼을 때, java.util.List 인지 java.awt.List 인지 구분할 수 없다.
    execution(public java.util.List<com.example.Myclass> *(..))
  • pointcut을 여러개 연결할 때는 &&, ||, !, and, or, not 등을 사용할 수 있다.
    @Before("execution(* com..*(..)) || execution(* org..*(..))")

execution 이외의 지정자

  • pointcut을 작성하면서 지시자로 execution 이외에도 within이나 bean, @annotation 등을 사용할 수도 있다.

  • within

    • 실제 타겟 빈의 타입을 기준으로 해당 클래스, 인터페이스 내의 모든 메서드를 pointcut으로 지정할 수 있다.

    • 빈에 적용된 애너테이션을 기준으로 pointcut을 지정할 때 사용된다.

      @Before("within(com.practice.service.*)") // com.practice.service 소속 빈들의 모든 메서드
      public void serviceBeans() {}
      
      @Before("@within(org.springframework.stereotype.Service)") //@Service 타입 빈들의 모든 메서드
      public void serviceBeans() {}
  • bean
    • 스프링의 빈이름을 이용하여 포인트 컷을 지정할 때 사용된다.

    • 빈의 구체적인 타입이나 클래스의 구조와 무관하게 빈의 이름만으로 pointcut을 지정한다.

      @Before("bean(*Service)")
      public void serviceBeans() {}
  • @annotation
    • 지정하는 특정 애너테이션이 선언된 메서드에 대해서 advice를 지정할 때 사용

      @Before("@annotation(org.springframework.transaction.annotation.Transactional)")
      public void transactionalMethods() {}

pointcut 재사용

  • 하나의 pointcut이 여러개의 advice에 적용될 수 있다면 pointcut을 저장해서 재사용 할 수 있다.
  • named pointcut이라고 하고 이를 위해 @Pointcut 애너테이션이 사용된다.
  • @Pointcut이 선언된 메서드의 이름은 pointcut id가 되고 advice를 선언할 때 이 pointcut id를 사용해야 하는 경우에 사용
  • 주의 사항
    • 메서드를 호출하는 형태로 호출해야 한다
    • 다른 클래스에 선언된 pointcut id를 사용해야 하는 경우 패키지를 포함한 전체 경로명을 써줘야 한다.
package com.practice.aop;

public class Pointcuts {
	@Pointcut("execution(* com.practice.service.*.*(..))")
	public void serviceLayerExecution() {
	}
}
package com.practice.aop;

@Aspect
@Component
public class LoggingAspect {
	
	@Before("com.practice.aop.Pointcuts.serviceLayerExecution()")
	public void logBefore(JoinPoint jp) {
	
	}
}

pointcut 작성 시 주의사항

정확한 범위 지정

  • 너무 많은 범위는 시스템 성능에 영향을 줄 수 있으며, 예상치 못한 결과를 얻을 수 있다.
  • 스프링이 관리하는 Bean은 우리가 만든 것 이외에도 아주 많고 그중 어떤 것의 어떤 메서드에 AOP가 적용되는지 알 수 없다. 따라서 와일드 카드를 남발하지 말고 적절한 패키지와 클래스를 이용한 제한은 필수적이다.
  • 중복 방지 : 여러 pointcut이 같은 메서드에 매칭될 경우 예상치 못한 동작이 발생할 수 있다. 따라서 pointcut을 설계할 때는 중복되지 않게 해야하고, 어쩔 수 없이 중복되는 경우에는 @Order을 통해 순서를 명시해야 한다.
  • 동적 프록시의 이해 : Spring의 AOP는 상속을 기반으로 동적 프록시를 생성한다. 따라서 final 클래스 / 메서드나 private 메서드는 AOP를 적용할 수 없다.
    • final 클래스나 메서드에서 AOP를 적용할 수 없는 이유
      • CGLIB는 클래스를 상속받아 프록시를 생성하므로, final로 선언된 클래스나 메서드는 상속이 불가능하기 때문에 프록시를 만들 수 없기 때문이다.
    • private 메서드에 AOP 적용 불가 이유
      • Spring AOP는 public 메서드에 기본적으로 적용된다. 프록시는 메서드 호출을 가로채서 부가 기능을 추가하는 방식인데, private은 외부에서 호출할 수가 없기 때문에 프록시가 이것을 가로채기 어렵기 때문이다.
profile
Back-End Developer

0개의 댓글

관련 채용 정보