2. @AspectJ support

2023. 5. 22. 15:10Spring Framework/Spring AOP

@AspectJ

@AspectJ는 애노테이션을 사용하여 일반적인 자바 클래스로 어드바이스를 선언하는 스타일을 의미합니다. @AspectJ 스타일은 AspectJ 5 릴리스의 일환으로 AspectJ 프로젝트에서 도입되었습니다. Spring은 포인트컷을 해석하고 매칭[match:연결]하기 위해 AspectJ에서 제공하는 라이브러리를 사용하여 동일한 애노테이션을 AspectJ 5처럼 해석합니다. 그러나 AOP 런타임은 여전히 순수한 Spring AOP로 동작하며, AspectJ 컴파일러나 위버(weaver)에 대한 의존성은 없습니다.

AspectJ 컴파일러와 위버를 사용하면 전체 AspectJ 언어를 사용할 수 있으며, 이에 대한 내용은 "Using AspectJ with Spring Applications"에서 다루고 있습니다.

@AspectJ라는 어노테이션은 Spring이나 AspectJ 프레임워크 자체에 존재하지 않습니다. 대신, "AspectJ"라는 용어는 Spring AOP 모듈에 비해 더 강력하고 고급 AOP 기능을 제공하는 특정한 애스펙트 지향 프로그래밍(AOP) 프레임워크를 의미합니다.

AspectJ vs. Spring AOP

  • AspectJ: AspectJ는 컴파일 시, 컴파일 후(바이너리 위빙), 그리고 로드 타임 위빙을 제공하는 완전한 AOP 프레임워크로, 애스펙트를 정의하고 적용하는 데 있어 광범위한 기능을 제공합니다. AspectJ는 Spring 빈뿐만 아니라 모든 Java 객체에 애스펙트를 적용할 수 있습니다.
  • Spring AOP: Spring AOP는 Spring 프레임워크의 일부로, 프록시 기반 AOP 프레임워크입니다. AspectJ의 기능 중 일부를 제공하며, 주로 Spring에서 관리되는 빈의 메서드 수준에서의 인터셉션에 제한됩니다.

@AspectJ 스타일

사람들이 "AspectJ 스타일"이라고 말할 때, 이는 일반적으로 Spring 내에서 애스펙트를 정의하기 위해 AspectJ 어노테이션을 사용하는 것을 의미합니다. Spring은 @Aspect, @Before, @After 등과 같은 AspectJ 스타일 어노테이션을 지원하며, 이들은 org.aspectj.lang.annotation 패키지에 속합니다. 이 스타일은 XML 구성 대신 어노테이션을 사용하여 애스펙트를 작성할 수 있게 하여 더 선언적이고 간결하게 만듭니다.

AspectJ의 주요 어노테이션 (Spring AOP에서 사용됨)

  • @Aspect: 클래스를 애스펙트로 표시합니다.
  • @Before: 특정 메서드가 실행되기 전에 실행될 advice를 정의합니다.
  • @After: 메서드의 실행 결과와 상관없이, 특정 메서드 실행 후에 실행될 advice를 정의합니다.
  • @Around: 메서드 실행을 감싸는 advice를 정의하여 실행 과정을 제어할 수 있게 합니다.
  • @AfterReturning: 메서드가 성공적으로 반환된 후에 실행될 advice를 정의합니다.
  • @AfterThrowing: 메서드가 예외를 던질 때 실행될 advice를 정의합니다.
  • @Pointcut: 재사용 가능한 포인트컷을 정의합니다.

@AspectJ라는 어노테이션은 존재하지 않지만, AspectJ 어노테이션은 Spring AOP 내에서 애스펙트를 정의하는 데 광범위하게 사용됩니다. 이러한 접근 방식은 Spring 문서에서 "AspectJ 스타일 어노테이션" 또는 "AspectJ 문법"으로 자주 언급됩니다.

Enabling @AspectJ Support

@AspectJ aspect를 기반으로 Spring AOP를 구성하고, 이 aspect에 의해 advice되는지 여부에 따라 빈을 auto-proxying하는 Spring 지원 기능을 활성화해야 합니다. auto-proxying는 Spring이 특정 빈이 하나 이상의 aspect에 의해 어드바이스된다고 판단할 경우, 해당 빈에 대해 메서드 호출을 가로채고 필요한 경우 어드바이스를 실행할 수 있도록 자동으로 프록시를 생성하는 것을 의미합니다.

@AspectJ 지원은 XML 또는 Java 기반 구성으로 활성화할 수 있습니다. 두 경우 모두 애플리케이션의 클래스패스에 AspectJ의 aspectjweaver.jar 라이브러리(버전 1.9 이상)를 포함해야 합니다. 이 라이브러리는 AspectJ 배포판의 lib 디렉토리나 Maven Central 저장소에서 사용할 수 있습니다.

Java 기반 구성으로 @AspectJ 지원 활성화

Java @Configuration을 사용하여 @AspectJ 지원을 활성화하려면, 다음 예시와 같이 @EnableAspectJAutoProxy 애노테이션을 추가합니다:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

XML 설정으로 @AspectJ 지원 활성화

XML 기반 구성으로 @AspectJ 지원을 활성화하려면, 다음 예시와 같이 <aop:aspectj-autoproxy> 요소를 사용합니다:

<aop:aspectj-autoproxy/>

이는 XML 스키마 기반 설정에서 설명된 스키마 지원을 사용하는 것을 전제로 합니다. aop 네임스페이스에서 태그를 가져오는 방법은 AOP 스키마를 참조하십시오.

Declaring an Aspect

@AspectJ 지원이 활성화된 상태에서, 애플리케이션 컨텍스트에 정의된 빈 중 클래스에 @Aspect 애노테이션이 있는 @AspectJ aspect은 Spring에 의해 자동으로 감지되며 Spring AOP를 구성하는 데 사용됩니다. 다음 두 가지 예시는 크게 유용하지 않은 aspect를 정의하는 데 필요한 최소 단계를 보여줍니다.

첫 번째 예시에서는 애플리케이션 컨텍스트에 있는 일반적인 빈 정의가 @Aspect 애노테이션이 있는 빈 클래스를 가리키는 방법을 보여줍니다:

<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
    <!-- 여기에서 측면의 속성을 구성 -->
</bean>

두 번째 예시에서는 @Aspect 애노테이션이 달린 NotVeryUsefulAspect 클래스 정의를 보여줍니다:

package com.xyz;

import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {
}

@Aspect로 애노테이션된 Aspect 클래스는 다른 클래스와 마찬가지로 메서드와 필드를 가질 수 있으며, 포인트컷, 어드바이스, 그리고 Introduction(인터타입) 선언을 포함할 수 있습니다.

Note

Autodetecting aspects through component scanning

클래스패스 스캔을 통해 Spring이 애스펙트 클래스를 자동으로 감지하도록 하거나, Spring XML 설정에서 일반 빈으로 등록하거나, @Configuration 클래스의 @Bean 메서드를 통해 등록할 수 있습니다. 그러나 @Aspect 애노테이션만으로는 클래스패스에서 자동 감지가 충분하지 않습니다. 이 경우에는 @Component 애노테이션(또는 Spring의 컴포넌트 스캐너 규칙에 따라 자격이 있는 사용자 정의 스테레오타입 애노테이션)을 추가해야 합니다.

Advising aspects with other aspects?
Spring AOP에서 aspect 자체는 다른 aspect의 어드바이스 대상이 될 수 없습니다. 클래스에 @Aspect 애노테이션이 붙으면 해당 클래스는 aspect로 표시되며, 따라서 auto-proxying 대상에서 제외됩니다.

Declaring a Pointcut

포인트컷은 관심 있는 조인 포인트를 결정하여 어드바이스가 실행될 시점을 제어할 수 있도록 합니다. Spring AOP는 Spring 빈에 대한 메서드 실행 조인 포인트만 지원하므로, 포인트컷은 Spring 빈의 메서드 실행을 매칭하는 것으로 생각할 수 있습니다. 포인트컷 선언은 이름과 파라미터를 포함하는 시그니처(signature)와 관심 있는 메서드 실행을 정확히 결정하는 포인트컷 표현식으로 구성됩니다. @AspectJ 애노테이션 스타일의 AOP에서는 포인트컷 시그니처가 일반 메서드 정의에 의해 제공되며, 포인트컷 표현식은 @Pointcut 애노테이션을 사용하여 표시됩니다(포인트컷 시그니처 역할을 하는 메서드는 void 반환 타입이어야 합니다).

다음 예시는 포인트컷 시그니처와 포인트컷 표현식 간의 차이를 명확히 하는 데 도움이 될 것입니다. 아래 예시는 transfer라는 이름의 메서드 실행을 매칭하는 anyOldTransfer라는 이름의 포인트컷을 정의합니다:

@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature

@Pointcut 애노테이션의 값으로 사용되는 포인트컷 표현식은 일반적인 AspectJ 포인트컷 표현식입니다. AspectJ의 포인트컷 언어에 대한 자세한 논의는 AspectJ 프로그래밍 가이드(및 확장 사항은 AspectJ 5 개발자 노트북) 또는 Eclipse AspectJ, AspectJ in Action과 같은 AspectJ 관련 서적을 참조하십시오.

Supported Pointcut Designators(지시어)

Spring AOP는 포인트컷 표현식에서 사용할 수 있는 다음과 같은 AspectJ 포인트컷 디지그네이터(PCD)를 지원합니다:

  • execution: 메서드 실행 조인 포인트를 매칭합니다. Spring AOP를 사용할 때 기본적으로 사용하는 포인트컷 디지그네이터입니다.
  • within: 특정 타입 내에서 조인 포인트를 제한합니다(Spring AOP를 사용할 때는 매칭되는 타입 내에서 선언된 메서드의 실행).
  • this: 빈 참조(Spring AOP 프록시)가 주어진 타입의 인스턴스인 경우 조인 포인트를 제한합니다(Spring AOP를 사용할 때 메서드 실행).
  • target: 타겟 객체(프록시된 애플리케이션 객체)가 주어진 타입의 인스턴스인 경우 조인 포인트를 제한합니다(Spring AOP를 사용할 때 메서드 실행).
  • args: 전달된 아규먼트가 주어진 타입의 인스턴스인 경우 조인 포인트를 제한합니다(Spring AOP를 사용할 때 메서드 실행).
  • @target: 실행 중인 객체의 클래스에 주어진 타입의 애노테이션이 있는 경우 조인 포인트를 제한합니다(Spring AOP를 사용할 때 메서드 실행).
  • @args: 런타임에 전달된 실제 아규먼트의 타입에 주어진 애노테이션이 있는 경우 조인 포인트를 제한합니다(Spring AOP를 사용할 때 메서드 실행).
  • @within: 주어진 애노테이션이 있는 타입 내에서 조인 포인트를 제한합니다(Spring AOP를 사용할 때 해당 애노테이션이 있는 타입에 선언된 메서드의 실행).
  • @annotation: 조인 포인트의 대상(즉, Spring AOP에서 실행 중인 메서드)에 주어진 애노테이션이 있는 경우 조인 포인트를 제한합니다.

Other pointcut types
전체 AspectJ 포인트컷 언어는 Spring에서 지원되지 않는 추가적인 포인트컷 디지그네이터를 지원합니다: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, 그리고 @withincode. Spring AOP가 해석하는 포인트컷 표현식에서 이러한 포인트컷 디지그네이터를 사용하면 IllegalArgumentException이 발생합니다.

Spring AOP가 매칭을 메서드 실행 조인 포인트에만 제한하기 때문에, 위에서 설명한 포인트컷 디지그네이터의 정의는 AspectJ 프로그래밍 가이드에서 찾을 수 있는 정의보다 좁은 의미를 갖습니다. 또한, AspectJ 자체는 type-based semantics 을 가지고 있으며, 실행 조인 포인트에서 this와 target은 동일한 객체를 참조합니다: 즉, 메서드를 실행하는 객체입니다. Spring AOP는 프록시 기반 시스템으로, 프록시 객체 자체(this에 바인딩됨)와 프록시 뒤의 타겟 객체(target에 바인딩됨)를 구분합니다.

Spring의 AOP 프레임워크는 프록시 기반이므로 타겟 객체 내의 호출은 정의상 가로채지 않습니다. JDK 프록시의 경우, 프록시에 대한 public 인터페이스 메서드 호출만 가로챌 수 있습니다. CGLIB의 경우, 프록시에 대한 public 및 protected 메서드 호출이 가로채지며(필요한 경우 package-visible메서드도 가로챌 수 있음) 하지만, 프록시를 통한 일반적인 상호 작용은 항상 public 시그니처를 통해 설계되어야 합니다.
포인트컷 정의는 일반적으로 가로채진 메서드에 대해 매칭됩니다. 포인트컷이 public 메서드에만 해당되도록 하려면, CGLIB 프록시 시나리오에서 프록시를 통한 non-public 상호 작용 가능성이 있는 경우에도, 이에 맞게 정의해야 합니다.
인터셉션이 타겟 클래스 내에서 메서드 호출이나 생성자를 포함해야 하는 경우, Spring의 프록시 기반 AOP 프레임워크 대신 Spring 주도의 native AspectJ weaving을 사용하는 것을 고려하십시오. 이는 다른 특성을 가진 AOP 사용 모드를 구성하므로, 결정을 내리기 전에 위빙에 대해 충분히 이해해야 합니다.

Spring AOP는 또한 bean이라는 추가 PCD를 지원합니다. 이 PCD는 특정 이름의 Spring 빈이나 와일드카드를 사용하여 명명된 Spring 빈 세트에 대해 조인 포인트 매칭을 제한할 수 있습니다. bean PCD는 다음과 같은 형태를 가집니다:

bean(idOrNameOfBean)

idOrNameOfBean 토큰은 어떤 Spring 빈의 이름이라도 될 수 있습니다. * 문자를 사용하는 제한된 와일드카드 지원이 제공되므로, Spring 빈에 대한 명명 규칙을 설정하면 이를 선택하는 bean PCD 표현식을 작성할 수 있습니다. 다른 포인트컷 디지그네이터의 경우와 마찬가지로, bean PCD도 &&(and), ||(or), !(부정) 연산자와 함께 사용할 수 있습니다.

bean PCD는 Spring AOP에서만 지원되며, 네이티브 AspectJ 위빙에서는 지원되지 않습니다. 이는 AspectJ가 정의한 표준 PCD에 대한 Spring 고유의 확장이므로 @Aspect 모델로 선언된 측면에는 사용할 수 없습니다.
bean PCD는 타입 수준에서만 작동하는 위빙 기반 AOP에 비해 인스턴스 수준에서 작동하며(Sring 빈 이름 개념을 기반으로), 특정 빈을 이름으로 식별하는 것이 자연스럽고 직관적인 Spring 프록시 기반 AOP 프레임워크 및 Spring 빈 팩토리와의 긴밀한 통합의 특별한 기능입니다.

Combining Pointcut Expressions

&&, ||!를 사용하여 포인트컷 표현식을 결합할 수 있습니다. 또한 포인트컷 표현식을 이름으로 참조할 수 있습니다. 다음 예시는 세 가지 포인트컷 표현식을 보여줍니다:

package com.xyz;

public class Pointcuts {

    @Pointcut("execution(public * *(..))")
    public void publicMethod() {}  1

    @Pointcut("within(com.xyz.trading..*)")
    public void inTrading() {}  2

    @Pointcut("publicMethod() && inTrading()")
    public void tradingOperation() {} 3
}
  1. publicMethod는 메서드 실행 조인 포인트가 public 메서드 실행을 나타내는 경우 매칭됩니다.
  2. inTrading은 메서드 실행이 trading 모듈 내에서 이루어진 경우 매칭됩니다.
  3. tradingOperation은 메서드 실행이 trading 모듈 내에서 수행되는 모든 public 메서드를 나타내는 경우 매칭됩니다.

위와 같이 더 복잡한 포인트컷 표현식을 더 작은 이름이 지정된 포인트컷으로 구성하는 것이 모범 사례입니다. 포인트컷을 이름으로 참조할 때는 일반적인 자바 가시성 규칙이 적용됩니다(동일한 타입에서 private 포인트컷을 볼 수 있으며, 상속 계층에서는 protected 포인트컷, 어디서나 public 포인트컷을 볼 수 있습니다). 가시성은 포인트컷 매칭에 영향을 미치지 않습니다.

Sharing Named Pointcut Definitions

엔터프라이즈 애플리케이션을 작업할 때, 개발자들은 애플리케이션 모듈과 특정 작업 세트를 여러 aspect에서 참조해야 하는 경우가 종종 있습니다. 이 목적을 위해 자주 사용되는 이름이 지정된 포인트컷 표현식을 캡처하는 전용 클래스를 정의하는 것이 좋습니다. 이러한 클래스는 일반적으로 다음의 CommonPointcuts 예시와 비슷하지만, 클래스 이름은 사용자에게 달려 있습니다:

package com.xyz;

import org.aspectj.lang.annotation.Pointcut;

public class CommonPointcuts {

    /**
     * 메서드가 com.xyz.web 패키지 또는 하위 패키지에 정의된 타입에 있는 경우, 조인 포인트는 웹 레이어에 있습니다.
     */
    @Pointcut("within(com.xyz.web..*)")
    public void inWebLayer() {}

    /**
     * 메서드가 com.xyz.service 패키지 또는 하위 패키지에 정의된 타입에 있는 경우, 조인 포인트는 서비스 레이어에 있습니다.
     */
    @Pointcut("within(com.xyz.service..*)")
    public void inServiceLayer() {}

    /**
     * 메서드가 com.xyz.dao 패키지 또는 하위 패키지에 정의된 타입에 있는 경우, 조인 포인트는 데이터 액세스 레이어에 있습니다.
     */
    @Pointcut("within(com.xyz.dao..*)")
    public void inDataAccessLayer() {}

    /**
     * 비즈니스 서비스는 서비스 인터페이스에 정의된 모든 메서드의 실행입니다.
     * 이 정의는 인터페이스가 "service" 패키지에 배치되고, 구현 타입이 하위 패키지에 있는 경우를 가정합니다.
     *
     * 기능별로 서비스 인터페이스를 그룹화하는 경우(예: com.xyz.abc.service 및 com.xyz.def.service 패키지에)
     * "execution(* com.xyz..service.*.*(..))" 포인트컷 표현식을 대신 사용할 수 있습니다.
     *
     * 또는, 'bean' PCD를 사용하여 표현식을 작성할 수도 있습니다.
     * 예: "bean(*Service)". (이 경우, Spring 서비스 빈의 이름이 일관되게 지정된다고 가정합니다.)
     */
    @Pointcut("execution(* com.xyz..service.*.*(..))")
    public void businessService() {}

    /**
     * 데이터 액세스 작업은 DAO 인터페이스에 정의된 모든 메서드의 실행입니다.
     * 이 정의는 인터페이스가 "dao" 패키지에 배치되고, 구현 타입이 하위 패키지에 있는 경우를 가정합니다.
     */
    @Pointcut("execution(* com.xyz.dao.*.*(..))")
    public void dataAccessOperation() {}

}

이와 같은 클래스에 정의된 포인트컷을 참조하여 포인트컷 표현식이 필요한 곳 어디에서나 사용할 수 있습니다. 예를 들어, 서비스 레이어를 트랜잭션 처리 가능하게 만들려면, 다음과 같이 com.xyz.CommonPointcuts.businessService()라는 이름의 포인트컷을 참조하여 작성할 수 있습니다:

<aop:config>
    <aop:advisor
        pointcut="com.xyz.CommonPointcuts.businessService()"
        advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<aop:config><aop:advisor> 엘리먼트에 대한 논의는 "Schema-based AOP Support"를 참조하십시오. 트랜잭션 엘리먼트는 "Transaction Management"에서 다룹니다.

Examples

Spring AOP 사용자는 주로 execution 포인트컷 디지그네이터를 가장 자주 사용할 것입니다. execution 표현식의 형식은 다음과 같습니다:

execution(modifiers-pattern?
          ret-type-pattern
          declaring-type-pattern?name-pattern(param-pattern)
          throws-pattern?)

리턴 타입 패턴(ret-type-pattern), 이름 패턴, 파라미터 패턴을 제외한 모든 부분은 선택 사항입니다. 리턴 타입 패턴은 조인 포인트가 매칭되기 위해 메서드의 리턴 타입이 무엇이어야 하는지를 결정합니다. * 은 가장 자주 사용되는 리턴 타입 패턴으로, 모든 리턴 타입을 매칭합니다. 완전히 지정된 타입 이름은 메서드가 해당 타입을 리턴하는 경우에만 매칭됩니다. 이름 패턴은 메서드 이름을 매칭합니다. * 와일드카드는 이름 패턴의 일부 또는 전체로 사용할 수 있습니다. 선언하는 타입 패턴을 지정하는 경우, 이름 패턴 구성 요소에 연결하기 위해 뒤에 .을 포함해야 합니다. 파라미터 패턴은 약간 더 복잡합니다: ()은 파라미터가 없는 메서드를 매칭하고, (..)은 임의의 수(0개 이상)의 파라미터를 매칭합니다. (*) 패턴은 하나의 파라미터를 가지는 메서드를 매칭하며, (*, String)은 두 개의 파라미터를 가지는 메서드를 매칭합니다. 첫 번째는 임의의 타입일 수 있으며, 두 번째는 반드시 String이어야 합니다. AspectJ 프로그래밍 가이드의 "Language Semantics" 섹션에서 더 많은 정보를 참조하십시오.

다음은 몇 가지 일반적인 포인트컷 표현식을 보여줍니다:

  • public 메서드의 실행:
execution(public * *(..))
  • 이름이 set으로 시작하는 모든 메서드의 실행:
execution(* set*(..))
  • AccountService 인터페이스에 정의된 모든 메서드의 실행:
execution(* com.xyz.service.AccountService.*(..))
  • service 패키지에 정의된 모든 메서드의 실행:
execution(* com.xyz.service.*.*(..))
  • service 패키지 또는 하위 패키지에 정의된 모든 메서드의 실행:
execution(* com.xyz.service..*.*(..))
  • service 패키지 내의 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
within(com.xyz.service.*)
  • service 패키지 또는 하위 패키지 내의 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
within(com.xyz.service..*)
  • 프록시가 AccountService 인터페이스를 구현하는 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
this(com.xyz.service.AccountService)
  • 타겟 객체가 AccountService 인터페이스를 구현하는 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
target(com.xyz.service.AccountService)
  • 런타임 시 전달된 인수가 Serializable 인스턴스인 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
args(java.io.Serializable)
  • 타겟 객체에 @Transactional 애노테이션이 있는 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
@target(org.springframework.transaction.annotation.Transactional)
  • 타겟 객체의 선언된 타입에 @Transactional 애노테이션이 있는 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
@within(org.springframework.transaction.annotation.Transactional)
  • 실행 중인 메서드에 @Transactional 애노테이션이 있는 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
@annotation(org.springframework.transaction.annotation.Transactional)
  • 런타임 시 전달된 아규먼트의 타입에 @Classified 애노테이션이 있는 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
@args(com.xyz.security.Classified)
  • 이름이 tradeService인 Spring 빈에 대한 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
bean(tradeService)
  • 이름이 *Service와 일치하는 Spring 빈에 대한 모든 조인 포인트(오직 Spring AOP에서 메서드 실행):
bean(*Service)

Writing Good Pointcuts

컴파일 과정에서 AspectJ는 포인트컷을 처리하여 매칭 성능을 최적화합니다. 코드를 분석하고 각 조인 포인트가 주어진 포인트컷과 일치하는지(정적 또는 동적) 판단하는 것은 비용이 많이 드는 과정입니다. (동적 매칭이란 정적 분석만으로는 매칭을 완전히 결정할 수 없으며, 코드가 실행될 때 실제 매칭 여부를 판단하기 위한 테스트가 코드에 삽입된다는 의미입니다.) 포인트컷 선언을 처음 접할 때, AspectJ는 이를 매칭 과정에 최적화된 형태로 재작성합니다. 이게 무슨 의미일까요? 기본적으로 포인트컷은 DNF(Disjunctive Normal Form, 쌍대 논리 형식)로 재작성되며, 포인트컷의 구성 요소가 평가 비용이 저렴한 순서로 정렬됩니다. 이는 다양한 포인트컷 디지그네이터의 성능을 이해하지 않아도 포인트컷 선언에서 자유롭게 순서를 지정할 수 있음을 의미합니다.

그러나 AspectJ는 주어진 정보로만 작업할 수 있습니다. 최적의 매칭 성능을 위해서는 달성하고자 하는 목표를 고려하고, 정의에서 매칭을 위한 검색 범위를 가능한 한 좁히는 것이 좋습니다. 기존의 디지그네이터는 자연스럽게 세 가지 그룹으로 나뉩니다: Kinded 지정, Scoping 지정, 그리고 Contextual 지정입니다.

  • Kinded 디지그네이터는 특정 종류의 조인 포인트를 선택합니다: execution, get, set, call, 그리고 handler.
  • Scoping 디지그네이터는 관심 있는 조인 포인트 그룹을 선택합니다(아마도 여러 종류): withinwithincode.
  • Contextual 디지그네이터는 문맥에 따라 매칭(및 선택적으로 바인딩)합니다: this, target, 그리고 @annotation.

잘 작성된 포인트컷은 최소한 앞의 두 가지 타입(Kinded 지정과 Scoping 지정)을 포함해야 합니다. contextual 지정을 포함하여 조인 포인트 컨텍스트에 따라 매칭하거나 그 컨텍스트를 어드바이스에서 사용할 수 있도록 바인딩할 수도 있습니다. Kinded 지정이나 Contextual 지정에만 제공하는 것도 작동하긴 하지만, 추가적인 처리와 분석 때문에 위빙 성능(시간과 메모리 사용량)에 영향을 미칠 수 있습니다. Scoping 지정은 매칭 속도가 매우 빠르며, 이를 사용하면 AspectJ가 더 이상 처리하지 않아야 하는 조인 포인트 그룹을 매우 빠르게 제외할 수 있습니다. 가능한 한 Scoping 지정을 항상 포함하는 것이 좋습니다.