5.4.3 Pointcut 선언하기 by ys

Pointcuts는 관심있는 조인 포인트를 결정하므로 advice를 실행할시기를 제어 할 수 있습니다. Spring AOP는 Spring 빈에 대한 메소드 실행 조인 포인트 만 지원하므로 Spring bean에서 메소드 실행과 pointcut이 일치한다고 생각할 수있다. 포인트 컷 선언은 이름과 매개 변수로 구성된 서명과 관심있는 메소드 실행을 정확하게 결정하는 pointcut 표현식의 두 부분으로 구성됩니다.

AOP의 @AspectJ 주석 스타일에서 포인트컷 신호는 일반 메소드 정의에 의해 제공되고 pointcut 표현식은 @Pointcut annotation을 사용하여 표시됩니다 (pointcut 서명 으로 사용 되는 메소드는 void반환 유형을 가져야합니다 ).

예를 들어 포인트 컷 신호와 포인트 컷 표현식을 구분할 수 있습니다. 다음 예제는 transfer라는 메서드의 실행과 일치하는 anyOldTransfer 라는 pointcut을 정의합니다.

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

@Pointcut annotation의 값을 형성하는 pointcut 표현식 은 일반적인 AspectJ 5 pointcut 표현식이다. AspectJ의 pointcut 언어에 대한 자세한 설명은 AspectJ Programming Guide ( AspectJ 5 Developer 's Notebook ) 또는 AspectJ에 관한 책 중 하나 (예 : Colyer et al.의 Eclipse AspectJ 또는 AspectJ in Action , Ramnivas Laddad에 의해).

지원되는 Pointcut 지정자

Spring AOP는 pointcut 표현식에서 사용하기 위해 다음과 같은 AspectJ pointcut designator (PCD)를 지원한다.

  • execution: 메소드 실행 조인 포인트 일치. 이것은 Spring AOP로 작업 할 때 사용할 기본 pointcut 지정자이다.

  • within: 특정 타입 내의 조인 포인트 (Spring AOP를 사용할 때 일치하는 타입 내에서 선언 된 메소드의 실행)와 일치하는 것을 제한합니다.

  • this: Bean 레퍼런스 (Spring AOP proxy)가 주어진 타입의 인스턴스 인 조인 포인트 (Spring AOP를 사용할 때 메소드의 실행)와의 매치 제한.

  • target: 대상 객체 (프록시 될 애플리케이션 객체)가 주어진 유형의 인스턴스 인 조인 포인트 (Spring AOP를 사용할 때 메소드 실행)와 일치하는 제한.

  • args: 인자가 주어진 타입의 인스턴스 인 조인 포인트 (Spring AOP를 사용할 때 메소드 실행)와 일치하는 제한.

  • @target: 실행 객체의 클래스가 주어진 타입의 annotation을 가지고있는 조인 포인트 (Spring AOP를 사용할 때 메소드의 실행)로 일치하는 것을 제한합니다.

  • @args: 전달 된 실제 인수의 런타임 유형에 주어진 유형의 annotation이있는 조인 포인트 (Spring AOP를 사용할 때 메소드 실행)와의 일치가 제한됩니다.

  • @within: 지정된 주석 (Spring AOP을 사용할 때 주어진 주석이있는 유형으로 선언 된 메소드의 실행) 내에서 조인 포인트와 일치하는 것을 제한합니다.

  • @annotation: 조인 포인트의 주제 (Spring AOP에서 실행되는 메소드)가 주어진 annotation을 갖는 조인 포인트로 일치하는 것을 제한합니다.

다른 pointcut 유형

: 전체의 AspectJ 포인트 컷 언어는 call, get, set, preinitialization,staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this,와 @withincode와 같이 spring에서 지원되지 않는 추가 포인트 컷 지정자 지원합니다.

Spring AOP에 의해 해석 된 pointcut 표현식에서 이러한 pointcut 지정자를 사용하면 결과 IllegalArgumentException가 던져진다.

Spring AOP가 지원하는 pointcut 지정자 세트는 향후 릴리스에서 확장되어 더 많은 AspectJ pointcut 지정자를 지원할 수있다.

Spring AOP는 메소드 실행 join point에만 매칭을 제한하기 때문에, 앞의 pointcut 지정자에 대한 설명은 AspectJ 프로그래밍 가이드에서 찾을 수있는 것보다 더 좁은 정의를 제공한다. 또한, AspectJ 자체는 타입 기반의 의미론을 가지고 있으며, 실행 joinpoint에서 thistarget은 동일한 객체, 즉 메소드를 실행하는 객체를 참조한다. Spring AOP는 프록시 기반 시스템이며 프록시 객체 자체 (this에 바인드 됨 )와 프록시 ( target에 바인드 됨 ) 뒤의 대상 객체를 구분합니다.

Spring AOP는 또한 추가로 bean으로 명명 된 PCD를 지원합니다. 이 PCD를 사용하면 특정 명명 된 Spring bean 또는 명명 된 Spring bean 집합 (와일드 카드 사용시)으로 조인 포인트 일치를 제한 할 수 있습니다. beanPCD의 형식은 다음과 같습니다

bean(idOrNameOfBean)

idOrNameOfBean토큰은 spring 빈의 이름이 될 수 있습니다. *문자 를 사용하는 제한된 와일드 카드 지원 이 제공되므로 Spring 빈에 대해 몇 가지 명명 규칙을 설정하면 bean PCD 표현식을 작성하여 선택할 수 있습니다. 다른 pointcut 지정자의 경우와 마찬가지로 beanPCD는 &&(및), ||(또는) 및 !(부정) 연산자 와 함께 사용할 수 있습니다 .

Pointcut 표현식 결합

당신은을 사용하여 pointcut 표현이 결합 될 수있다 결합 할 수 있습니다 &&, ||!. 포인트 컷 식을 이름으로 참조 할 수도 있습니다. 다음 예제에서는 세 가지 pointcut 표현식을 보여줍니다.

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} 

@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {} 

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} 

앞에서 설명한 것처럼 더 작은 명명 된 구성 요소에서보다 복잡한 pointcut 식을 만드는 것이 좋습니다. pointcuts를 이름으로 언급 할 때 일반적인 Java 가시성 규칙이 적용됩니다 (동일한 유형의 개인 pointcuts, 계층의 보호 된 pointcuts, 어디에서나 public pointcuts 등을 볼 수 있음). 가시성은 pointcut 매칭에 영향을 미치지 않습니다.

일반적인 Pointcut 정의 공유

개발자는 엔터프라이즈 응용 프로그램을 사용하여 작업 할 때 응용 프로그램의 모듈과 특정 작업 집합을 여러 측면에서 참조하기를 원할 때가 있습니다. 이 목적을 위해 일반적인 pointcut 표현식을 캡처하는 "SystemArchitecture"측면을 정의하는 것이 좋습니다. 이러한 측면은 일반적으로 다음 예제와 유사합니다.

package com.xyz.someapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {

    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.xyz.someapp.web package or any sub-package
     * under that.     
     메서드가 com.xyz.someapp.web 패키지 또는 
     그 아래의 하위 패키지에있는 유형으로 정의 된 경우 조인 포인트가 웹 레이어에 있습니다.
     */
    @Pointcut("within(com.xyz.someapp.web..*)")
    public void inWebLayer() {}

    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.xyz.someapp.service package or any sub-package
     * under that.
메소드가 com.xyz.someapp.service 패키지 또는 그 아래의 하위 패키지에있는 유형으로 
정의 된 경우 조인 포인트가 서비스 계층에 있습니다.
     */
    @Pointcut("within(com.xyz.someapp.service..*)")
    public void inServiceLayer() {}

    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.xyz.someapp.dao package or any sub-package
     * under that.
     
메서드가 com.xyz.someapp.dao 패키지 또는 그 아래의 하위 패키지에있는 유형으로 정의 된 경우 
조인 포인트가 데이터 액세스 레이어에 있습니다.
     */
    @Pointcut("within(com.xyz.someapp.dao..*)")
    public void inDataAccessLayer() {}

    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     *
     * If you group service interfaces by functional area (for example,
     * in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then
     * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
     * could be used instead.
     *
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
비즈니스 서비스는 서비스 인터페이스에 정의 된 모든 메소드의 실행입니다.
이 정의는 인터페이스가 "service"패키지에 있고 구현 유형이 하위 패키지에 있다고 가정합니다. 
기능 영역별로 서비스 인터페이스를 그룹화하는 경우 
(예 : com.xyz.someapp.abc.service 및 com.xyz.someapp.def.service 패키지)
대신 pointcut 표현 "execution (* com.xyz.someapp..service. *. * (*))"를 
사용할 수 있습니다.
또는 "bean (* Service)"처럼 'bean'PCD를 사용하여 표현식을 작성할 수 있습니다. 
(이것은 당신이 일관된 방식으로 Spring 서비스 빈의 이름을 지정했다고 가정한다.)
     */
    @Pointcut("execution(* com.xyz.someapp..service.*.*(..))")
    public void businessService() {}

    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     
데이터 액세스 작업은 DA 인터페이스에서 정의 된 모든 메소드의 실행입니다. 
이 정의는 인터페이스가 "dao"패키지에 있고 구현 유형이 하위 패키지에 있다고 가정합니다.
     */
    @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
    public void dataAccessOperation() {}

}

포인트 컷식이 필요한 곳이면 어디서나 정의 된 pointcut를 참조 할 수 있습니다. 예를 들어 서비스 계층을 트랜잭션 방식으로 만들려면 다음을 작성할 수 있습니다.

<aop:config>
    <aop:advisor
        pointcut="com.xyz.someapp.SystemArchitecture.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>요소에서 설명 스키마 기반의 AOP 지원 . 트랜잭션 요소는 트랜잭션 관리 에서 설명 합니다.

예제들

Spring AOP 사용자는 executionpointcut 지정자를 가장 자주 사용합니다. 실행 식의 형식은 다음과 같습니다.

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

반환되는 유형 패턴 ( 앞의 스 니펫에서ret-type-pattern), 이름 패턴 및 매개 변수 패턴을 제외한 모든 부분 은 선택 사항입니다. 반환 형식 패턴은 조인 지점을 일치시키기 위해 메서드의 반환 형식이 무엇인지 결정합니다. *가 반환 유형 패턴으로 가장 많이 사용됩니다. 모든 반환 유형과 일치합니다. 정규화 된 형식 이름은 메서드가 지정된 형식을 반환하는 경우에만 일치합니다. 이름 패턴은 메소드 이름과 일치합니다. *와 와일드 카드는 이름 패턴의 전체 또는 사용할 수 있습니다 . 선언 형 패턴을 지정하는 경우, 후행 .을 포함시켜 이를 이름 패턴 구성 요소에 결합하십시오.

매개 변수 패턴은 약간 더 복잡합니다. ()는 매개 변수를 사용하지 않는 메소드와 일치하는 반면,(..)모든 숫자 (0 또는 그 이상의) 매개 변수와 일치합니다.

(*)패턴은 임의의 타입의 하나 개의 매개 변수를 일치시키는 방법. (*,String)는 두 개의 매개 변수를 사용하는 메소드와 일치합니다

. 첫 번째 유형은 모든 유형이 될 수 있지만 두 번째 유형은 String 유형이어야합니다. 더 자세한 정보는 AspectJ Programming Guide 의 Language Semantics 섹션을 참고하십시오.

다음 예제는 일반적인 pointcut 표현식을 보여줍니다 :

  • 모든 public 메소드의 실행 :

    execution(public * *(..))
  • 다음으로 시작하는 이름을 가진 메소드 실행 set:

    execution(* set*(..))
  • AccountService인터페이스에 의해 정의 된 모든 메소드의 실행 :

    execution(* com.xyz.service.AccountService.*(..))
  • service패키지에 정의 된 모든 메소드 실행 :

    execution(* com.xyz.service.*.*(..))
  • 서비스 패키지 또는 하위 패키지 중 하나에 정의 된 메소드 실행 :

    execution(* com.xyz.service..*.*(..))
  • 서비스 패키지 내의 모든 조인 포인트 (Spring AOP에서만 메소드 실행) :

    within(com.xyz.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)
  • 이 예제에서 주어진 포인트 컷은 execution(* *(java.io.Serializable))와 다르다. args 버전은 런타임에 전달 된 인수가Serializable이고, 메소드 서명이 Serializable type의 단일 매개 변수를 선언하면 실행 버전이 일치하는 경우 일치합니다.

  • 대상 객체에 @Transactionalannotation 이있는 모든 조인 포인트 (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 bean에서 join point (Spring AOP에서만 메소드 실행):

bean(tradeService)
  • 와일드 카드 표현식과 일치하는 이름을 가진 Spring 빈에 대한 모든 조인 포인트 (Spring AOP에서만 메소드 실행) *Service:

    bean(*Service)

좋은 Pointcuts 작성하기

컴파일하는 동안 AspectJ는 일치하는 성능을 최적화하기 위해 pointcut을 처리한다. 코드를 검사하고 각 조인 포인트가 주어진 pointcut과 일치하는지 (정적 또는 동적으로) 결정하는 것은 값 비싼 프로세스입니다. 동적 일치는 정적 분석에서 일치를 완전히 결정할 수 없으며 코드가 실행 중일 때 실제 일치가 있는지 확인하기 위해 코드에 테스트가 배치된다는 의미입니다. AspectJ는 먼저 pointcut 선언을 만나면 그것을 일치하는 프로세스를위한 최적의 형태로 다시 작성한다. 이것은 무엇을 의미 하는가? 기본적으로 pointcut는 DNF (Disjunctive Normal Form)로 다시 작성되며 pointcut의 구성 요소는 평가하기에 더 저렴한 구성 요소가 먼저 확인되도록 정렬됩니다.

그러나, AspectJ는 그것이 무엇을 말했는지에만 작용할 수있다. 최적의 일치 성능을 얻으려면 자신이 성취하려는 대상에 대해 생각하고 일치하는 검색 공간을 최대한 정의해야합니다. 현존하는 지정자는 자연히 kinded, scoping 및 contextual의 세 그룹 중 하나로 분류됩니다.

  • Kinded 지정자 포인트 조인의 특정 종류를 선택 : execution, get, set, call, 및handler .

  • Scoping 지정자는 관심있는 조인 포인트 그룹을 선택합니다 (아마도 여러 종류가 있습니다 : within 하고withincode

  • 상황에 따라 문맥 지정자 일치 (및 선택적으로 바인드) : this, target@annotation

잘 쓰여진 pointcut은 적어도 처음 두가지 유형 (kinded와 scoping)을 포함해야한다. 컨텍스트 지정자를 포함하여 조인 포인트 컨텍스트를 기반으로 일치 시키거나 해당 컨텍스트를 바인딩하여 조언에 사용할 수 있습니다. kinded 지정 자나 컨텍스트 지정자 만 작동하지만 추가 처리 및 분석으로 인해 성능 (시간 및 메모리 사용)에 영향을 미칠 수 있습니다. 스코프 지정자는 매우 빠르게 매칭되며, 사용법을 사용하면 AspectJ는 더 이상 처리되지 않아야하는 조인 포인트 그룹을 매우 빠르게 해제 할 수 있습니다. 가능한 한 좋은 pointcut은 항상 하나를 포함해야합니다.

Last updated