|
AspectJ의 Pointcut 표현식 정리
* Execution 문법
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?namepattern(param-pattern) throws-pattern?)
execution(접근제어자? 반환타입 선언타입?메서드이름(파라미터) 예외?)
* Execution 참고
- 메소드 실행 조인 포인트를 매칭한다.
- ?는 생략할 수 있다. (접근제어자, 선언타입, 예외)
- *같은 패턴을 지정할 수 있다.
- 파라미터에서 ..은 파라미터의 타입과 파라미터 수가 상관없단 의미.
* Execution 파라미터 매칭 규칙
- (String) : 정확하게 String 타입 파라미터
- () : 파라미터가 없어야 한다.
- (*) : 정확히 하나의 파라미터, 단 모든 타입을 허용.
- (*, *) : 정확히 두 개의 파라미터, 단 모든 타입을 허용.
- (..) : 숫자와 무관하게 모든 파라미터, 모든 타입을 허용. 참고로 파라미터가 없어도 된다. 0..* 로 이해하면 된다.
- (String, ..) : String 타입으로 시작해야 한다. 숫자와 무관하게 모든 파라미터, 모든 타입을 허용.
예) (String) , (String, Xxx) , (String, Xxx, Xxx) 허용
execution([접근자제어패턴], 리턴타입패턴 [패키지패턴] 메서드이름패턴(파라메터패턴)) [ ] 안의 패턴은 생략 가능
execution(public void set*(..))
public에 리턴값이 없으며, 패키지명은 없고, 메서드는 set으로 시작하며 인자값은 0개 이상인 메서드 호출
execution(* com.people.*.*())
리턴타입에 상관없이 com.people패키지의 인자값이 없는 모든 메서드 호출
execution(* com.people..*.*(..))
리턴타입에 상관없이 com.people 패키지 및 하위 패키지에 있는, 인자값이 0개 이상인 메서드 호출
execution(Integer com.people.WriteService.write(..))
리턴 타입이 Integer인 WriteServlce의 인자값이 0개 이상인 write() 호출
execution(* *..*HelloInter*.*(..)))
특정 인터페이스에서 정의된 메소드가 대상
execution(* *..*HelloInter*.*(..)) || execution(public void selectAll())
execution을 복수로 기술 가능 ||, or / &&, and
execution(* get*(*))
메서드 이름이 get으로 시작하는 인자값이 1개인 메서드 호출
execution(* get*(*,*))
메서드 이름이 get으로 시작하는 인자값이 2개인 메서드 호출
execution(* get*(Integer, ..))
메서드 이름이 get으로 시작하고 첫번째 인자값의 데이터타입이 Integer이며, 1개 이상의 인자값을 갖는 메서드 호출
execution(* com..*(..)) && @annotation(@annotation)
@annotation이 있는 모든 메소드 호출
execution(* *(..,@annotation (*), ..))
@annotation을 파라메터로 갖고 있는 모든 메소드 호출
//추가
AspectJ의 Pointcut 표현식
□ POJO 클래스를 이용하여 AOP를 적용하는 두 가지 방법
- XML 스키마를 이용하여 Aspect를 설정하는 방법.
- @Aspect 어노테이션을 이용하여 Aspect를 설정하는 방법.
■ 두 방법의 공통점
- AspectJ의 문법을 이용하여 Pointcut을 설정.
■ <aop:태그>를 이용하여 Aspect를 설정하는 경우
- execution 명시자를 이용하여 Advice가 적용될 Pointcut을 설정.
<aop:aspect id="cacheAspect" ref="cache">
<aop:around method="read" pointcut="execution(public * kk.spring.chap03.core.*.readArticle(..))" />
</aop:aspect>
□ AspectJ의 Pointcut 표현식
- AspectJ는 Pointcut을 명시할 수 있는 다양한 명시자를 제공.
- 스프링은 메서드 호출과 관련된 명시자만을 지원.
■ execution 명시자
- Advice를 적용할 메서드를 명시할 때 사용.
○ 기본 형식
execution(수식어패턴? 리턴타입패턴 패키지패턴?이름패턴(파라미터패턴)
● 수식어 패턴
- 생략가능한 부분.
- public, protected 등이 옴.
● 리턴타입패턴
- 리턴 타입을 명시
● 클래스이름 패턴, 이름패턴
- 클래스 이름 및 메서드 이름을 패턴으로 명시.
● 파라미터패턴
- 매칭될 파라미터에 대해서 명시.
○ 특징
- 각 패턴은 '*'를 이용하여 모든 값을 표현.
- '..'을 이용하여 0개 이상이라는 의미를 표현.
○ 설정 예
● execution(public void set*(..))
- 리턴 타입이 void이고 메서드 이름이 set으로 시작하고, 파라미터가 0개 이상인 메서드 호출.
● execution(* kk.spring.chap03.core.*.*())
- kk.spring.chap03.core 패키지의 파라미터가 없는 모든 메서드 호출.
● execution(*.kk.spring.chap03.core..*.*(..))
- kk.spring.chap03.core 패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 메서드 호출.
● execution(Integer kk.spring.chap03.core.WriteArticleService.write(..))
- 리턴 타입이 Integer인 WriteArticleService 인터페이스의 write() 메서드 호출.
● execution(* get*(*))
- 이름이 get으로 시작하고 1개의 파라미터를 갖는 메서드 호출.
● execution(* get*(*, *))
- 이름이 get으로 시작하고 2개의 파라미터를 갖는 메서드 호출.
● execution(* read*(Integer, ..))
- 메서드 이름이 read로 시작하고, 첫 번째 파라미터 타입이 Integer이며, 1개 이상의 파라미터를 갖는 메서드 호출.
■ within 명시자
- 메서드가 아닌 특정 타입에 속하는 메서드를 Pointcut으로 설정할 때 사용.
○ 설정 예
● within(kk.spring.chap03.core.WriteArticleService)
- WriteArticleService 인터페이스의 모든 메서드 호출.
● within(kk.spring.chap03.core.*)
- kk.spring.chap03.core 패키지에 있는 모든 메서드 호출.
● within(kk.spring.chap03.core..*)
- kk.spring.chap03.core 패키지 및 그 하위 패키지에 있는 모든 메서드 호출.
■ bean 명시자
- 스프링 2.5 버전부터 스프링에서 추가적으로 제공하는 명시자.
- 스프링 빈 이름을 이용하여 Pointcut을 정의.
- 빈 이름의 패턴을 갖는다.
○ 설정 예
● bean(writeArticleService)
- 이름이 writeArticleService인 빈의 메서드 호출.
● bean(*ArticleService)
- 이름이 ArticleServie로 끝나는 빈의 메서드 호출.
■ 표현식 연결
- 각각의 표현식은 '&&' 나 '||' 연산자를 이용하여 연결 가능.
○ @Aspect 어노테이션을 이용하는 경우
- '&&' 연산자를 사용하여 두 표현식을 모두 만족하는 Joinpoint에만 Advice가 적용.
@AfterThrowing(
pointcut = "execution(public * get*()) && execution(public void set*(..))")
public void throwingLogging() {
...
}
○ XML 스키마를 이용하여 Aspect를 설정하는 경우
- '&&'나 '||' 연산자를 사용.
<aop:pointcut id="propertyMethod" expression="execution(public * get*()) && execution(public void set*(..))" />
- XML 문서이기 때문에 값에 들어가는 '&&' '||'를 '&&'로 표현.
- 설정파일에서 '&&'나 '||' 대신 'and'와 'or'를 사용할 수 있도록 하고 있음.
<aop:pointcut id="propertyMethod" expression="execution(public * get*()) and execution(public void set*(..))" />
□ 프록시 구현 방식에 따른 execution 적용 차이
- Pointcut은 실제로 프록시가 구현하고 있는 인터페이스를 기준으로 Pointcut을 적용.
- 인터페이스를 구현하고 있는 대상 객체에 대해서 Pointcut을 정의하려면 인터페이스를 기준으로 Pointcut을 작성.
<aop:aspect id="cacheAspect" ref="cache">
<aop:around method="read" pointcut="execution(public * kk..core.ReadArticleServiceImpl.get*(..))" />
</aop:aspect>
<bean id="readArticleService" class="kk.spring.chap03.core.ReadArticleServiceImpl" />
- ReadArticleServiceImpl 클래스가 ReadArticleService 인터페이스를 구현하고 있다면,
<aop:around>는 ReadArticleServiceImpl 클래스의 get으로 시작하는 메서드에 적용.
(<aop:around> 태그에서 명시한 Pointcut은 readArticleService 빈에는 적용되지 않음.)
- ReadArticleServiceImpl 클래스가 인터페이스를 구현하고 있지 않다면,
생성된 프록시는 ReadArticleServiceImpl 클래스를 상속받아 생성됨. (Pointcut은 readArticleService 빈에만 적용.)
[출처] 3. 스프링 AOP (AspectJ의 Pointcut 표현식)|작성자 외계인셩
설명 하나 더 참조합니다. 출처 : http://springmvc.egloos.com/504448
포인트컷 표현식은 execution으로 시작합니다. 포인트 컷의 표현식에서 사용자를 헷갈리게 하는 것은 '*'과 '..'인데 위의 그림에서 보이 듯 '*'와 '..'가 문맥에 따라 역할이 달라집니다. 이 부분은 예를 들어가며 설명하는 것이 가장 최선일 듯 합니다.
첫번째 예제
① execlution(* meth(..))
② execlution(* meth())
③ execlution(* meth(int, int))
④ execlution(* meth*(..))
먼저 ①번 표현식에서 가장 좌측의 '* '는 어플리케이션에 존재하는 모든 라이브러리에서 meth라는 메서드를 뜻합니다. 어떤 라이브러리든 가리지 않고 meth라는 이름의 메서드는 모두 선택하게 되죠. 그리고 가장 우측에 '(..)'는 meth라는 메서드에 인자가 있든 없든 상관하지 않는다는 뜻입니다. 즉 같은 맥락에서 ②, ③번은 각각 인자가 포함되지 않은 meth메서드를 가리키는 것과 int인자가 둘 있는 것을 뜻하게 되죠. ④번은 meth로 메서드 이름에 와일드카드를 선택하는 경우입니다. 모든 라이브러리에서 meth, 또는 meth라는 이름으로 시작된 메서드를 뜻합니다.
두번째 예제
① execlution(* danzka.vodka.UserDao.*(..))
② execlution(* danzka.vodka.*.*(..))
③ execlution(* danzka.vodka..*.*(..))
④ execlution(* *..*Service(..))
이 예제들은 모두 클래스나 패키지를 기준으로 표현식을 작성하는 방법입니다. 이런 클래스나 패키지로 표현식을 작성할 때 유의할 점은 좌측의 '* '이 루트 패키지건 기본 패키지건 상관없이 의무적으로 삽입되야 한다는 것입니다. ①번은 직접 UserDao란 클래스를 지정해 사용하고 있으며 ②은 vodka란 패키지의 모든 클래스, 모든 메서드를 뜻합니다. 그리고 ③은 vodka 내의 모든 패키지, 모든 클래스, 모든 메서드를 뜻합니다. 첫번째 예제와 두번째 예제를 응용해 본다면 ④번과 같이 모든 패키지와 클래스 중에 Service로 시작하는 메서드를 선택한다는 것도 가능하겠지요.
세번째 예제
① execlution(* *..Interface.*(..))
② execlution(* *(..) throws SQL*)
③ execlution(String *(..))
④ execlution(void *(..))
①번은 좀 헷갈리겠지만 두번째 예제에서 ①번이 클래스를 선택한 것이라면 이번 예제의 ①은 인터페이스를 선택한 것을 뜻합니다. (물론 해당 이름의 클래스가 있다면 그 클래스도 선택) 물론 위의 이름이 Interface란 것은 실제 Interface를 선택하려는 명명 규칙과는 아무 상관 없습니다. 자신이 구현한 인터페이스 이름이 vazuka라면 execlution(* *..Vazuka.*(..))와 같이 사용할 수도 있겠죠. 다만 여기서 표현하고자하는 것은 위의 인터페이스를 구현한 모든 클래스의 메서드를 선택할 수 있다는 뜻입니다.
②번은 예외를 기준으로 선택하는 것인데 정말 극단적으로 AspectJ의 선택 폭이 넓다는 것을 뜻하기도 합니다. SQL의 끝에 와일드 카드를 붙여 라이브러리의 모든 메서드에서 SQL*의 예외를 발생시키는 것을 선택하는 방식은 정말 기발하면서도 감탄스럽기도 하네요 ^^; 마지막으로 ③, ④는 리턴 값으로 찾아내는 방법인데 같은 맥락에서 그동안 사용했던 가장 좌측 '* '를 해석해보자면 결국 모든 리턴 값을 선택한다는 뜻이라 할 수 있겠네요.
이처럼 AspectJ에서 제공하는 포인트컷 표현식을 사용하면 굉장히 효율적이고 적은 량의 코딩을 가능하게 해줍니다