AOP에는 새로운 용어가 많이 등장한다. 이 중에서 특히 AOP를 이용해서 개발하는데 필요한 다음의 주요 구성 요소들에 대해 정확한 이해가 필요하다.
JoinPoint
Crosscutting Concerns 모듈이 삽입되어 동작할 수 있는 실행 가능한 특정 위치를 말한다.
예를 들어 메소드가 호출되는 부분 또는 리턴되는 시점이 하나의 JoinPoint가 될 수 있다. 또 필드를 액세스하는 부분,
인스턴스가 만들어지는 지점, 예외가 던져지는 시점, 등이 대표적인 JoinPoint가 될 수 있다.
각각의 JoinPoint들은 그 전후로 Crosscutting Concerns의 기능이 AOP에 의해 자동으로 추가되어져서 동작할 수 있는
후보지가 되는 것이다.
Pointcut
Pointcut은 어느 JoinPoint를 사용할 것인지를 결정하는 선택 기능을 말한다.
AOP가 항상 모든 모듈의 모든 JoinPoint를 사용할 것이 아니기 때문에 필요에 따라 사용해야 할 모듈의 특정 JoinPoint를 지정할 필요가
있다. 일종의 JoinPoint 선정 룰과 같은 개념으로 다음과 같은 Pattern Matching 방법들을 이용하여 룰을 정의할 수 있다.
Pattern Matching Examples
1. Basics
- set*(..)
: set으로 시작하는 모든 메소드명
- * main(..)
: return type이 any type이고, 0개 이상의 any type parameter를 가진 main 메소드
2. Matching Type
- java.io.*
: java.io 패키지 내에 속한 모든 요소
- org.myco.myapp..*
: org.myco.myapp 패키지 또는 서브 패키지 내에 속한 모든 요소
- Number+
: Number 또는 Number의 서브 type으로 Integer, Float, Double ..등이 이에 해당
- !(Number+)
: Number 또는 Number의 서브 type이 아닌 모든 type
- org.xyz.myapp..* && !Serializable+
: org.xyz.myapp 패키지 또는 서브 패키지 내에 존재하면서 Serializable type이 아닌 모든 요소
- int || Integer
: int 또는 Integer type
3. Matching Modifiers
- public static void main(..)
: 0개 이상의 any type parameter를 가진 public static void main 메소드
- !private * *(..)
: return type이 any type이고, 0개 이상의 any type parameter를 가진 모든 메소드중 modifier가 private이 아닌 메소드
- * main(..)
: modifier를 별도로 명시하지 않은 경우, default modifier가 아닌 any modifier 의미
4. Matching Parameter
- * main(*)
: return type이 any type이고, 1개의 any type parameter를 가진 main 메소드
- * main(*,..)
: return type이 any type이고, 최소 1개의 any type parameter를 가진 main 메소드
- * main(*,..,String,*)
: return type이 any type이고, 최소 3개의 any type parameter를 가지며 끝에서 두번째 parameter type이 String인 main 메소드
5. Matching Constructor
- new(..)
: 0개 이상의 any type parameter를 가진 constructor
- Account.new(..)
: 0개 이상의 any type parameter를 가진 Account 클래스의 constructor
AspectJ는 Pointcut을 명시할 수 있는 다양한 Pointcut Designator(지시자)를 제공한다. 이제부터 앞서 정의한 Pattern Matching
방법을 이용하여, 본격적으로 Pointcut Designator별 Pointcut 정의 방법에 대해 살펴보기로 하자.
Pointcut Designators
1. execution 또는 call
특정 메소드나 생성자 실행을 위한 JoinPoint를 정의하는 것으로, JoinPoint의 특정 method name, parameter types,
return type, declared exceptions, declaring type, modifiers에 대한 matching이 가능하며,
단, return type pattern, method name pattern, parameter list pattern은 필수적으로 정의해야 한다.
다음은 execution, call을 이용한 pointcut 정의 예이다.
- execution(* main(..))
: return type이 any type이고, 0개 이상의 any type parameter를 가진 main 메소드 실행시
- call(Account.new(..))
: any type parameter를 가진 Account 클래스의 constructor 호출시
2. get 또는 set
특정 Field에 접근하거나 특정 Field 수정을 위한 JoinPoint를 정의한다.
- get(Collection+ org.xyz.myapp..*.*)
: Collection type의 org.xyz.myapp 패키지에 속한 any field에 대한 getter 호출시
- set(!private * Account+.*)
: Account type의 non-private field에 대한 setter 호출시
3. handler
Exception 핸들링을 위한 JoinPoint를 정의한다.
- handler(DataAccessException)
: matches cach(DataAccessException){...} and doesn't match catch(RuntimeException)
- handler(RuntimeException+)
: matches both
4. within
특정 유형에 속하는 JoinPoint를 정의하며, 주로 &&, ||, ! 등과 함께 조합된 형태로 사용된다.
- within(*)
: matches any JoinPoint
- within(org.xyz.myapp..*)
: org.xyz.myapp 패키지 내에 속하는 모든 요소
- within(IInterface+)
: IInterface type의 모든 요소
5. withincode
해당되는 메소드 또는 constructor 내에 정의된 코드를 위한 JoinPoint를 정의한다.
- withincode(!void get*())
: return type이 void가 아니고 메소드명이 get으로 시작하며 parameter가 없는 메소드 내의 코드
6. args
입력값의 개수, type 등에 대한 JoinPoint를 정의한다.
- call(* transfer(..)) && args(DepositAccount,CheckingAccount,*)
: 메소드명이 transfer이고, 입력 인자가 2개 이상이며, 1,2번째 입력 인자의 type이 DepositAccount,CheckingAccount인 메소드 호출시
7. this
JoinPoint를 가진 object의 type을 정의한다. (Runtime type)
- this(Account)
: 인터페이스 Account를 구현한 클래스(Proxy)의 모든 JoinPoint
8. target
JoinPoint를 가진 target object의 type을 정의한다. (Runtime type)
- call(* *(..)) && target(Account)
: Account 클래스 내의 모든 메소드 호출시
Spring은 메소드 호출 부분에 대한 AOP만을 지원하므로, 위에 정의한 다양한 Pointcut Designator 중 execution, within, target, this, args만이
사용 가능하다.
Weaving 또는 CrossCutting
AOP가 Core Concerns 모듈의 코드를 직접 건드리지 않고 필요한 기능이 작동하도록 하는 데는
Weaving 또는 CrossCutting이라고 불리는 특수한 작업이 필요하다.
Core Concerns 모듈이 자신이 필요한 Crosscutting Concerns 모듈을 찾아 사용하는 대신에 AOP에서는 Weaving 작업을 통해 Core Concerns 모듈의 사이 사이에 필요한
Crosscutting Concerns 코드가 동작하도록 엮어지게 만든다.
이를 통해 AOP는 기존의 OOP로 작성된 코드들을 수정하지 않고도 필요한 Crosscutting Concerns 기능을 효과적으로 적용해 낼 수 있다.

Weaving은 기존의 자바 언어와 컴파일러에서는 쉽게 구현할 수 있는 방법이 아니었으며 본격적인 AOP 기술이 등장한
것은 1990년대 후반 제록스 PARC 연구소에서 그레거 킥제일(Gregor Kiczales)에 의해 AspectJ가 개발되면서라고 볼 수 있다.
Weaving을 처리하는 방법은 다음과 같이 3가지가 존재한다.
| Weaving 방식
|
설명
|
| Compiletime Weaving
|
별도 컴파일러를 통해 Core Concerns 모듈의 사이 사이에
Aspect 형태로 만들어진 Crosscutting Concerns 코드들이 삽입되어 Aspect가 적용된 최종
바이너리가 만들어지는 방식이다. (ex. AspectJ, ...) |
| Loadingtime Weaving
|
별도의 Agent를 이용하여 JVM이 클래스를 로딩할 때 해당 클래스의 바이너리 정보를 변경한다.
즉, Agent가 Crosscutting Concerns 코드가 삽입된 바이너리 코드를 제공함으로써 AOP를 지원하게
된다. (ex. AspectWerkz, ...) |
| Runtime Weaving
|
소스 코드나 바이너리 파일의 변경없이 Proxy를 이용하여 AOP를 지원하는 방식이다. Proxy를 통해
Core Concerns를 구현한 객체에 접근하게 되는데, Proxy는 Core Concerns 실행 전후에 Cross Concerns를 실행한다.
따라서 Proxy 기반의 Runtime Weaving의 경우 메소드 호출시에만 AOP를 적용할 수 있다는 제한점이 있다.
(ex. Spring AOP, ...) |
Aspect
Aspect는 어디에서(Pointcut) 무엇을 할 것인지(Advice)를 합쳐놓은 것을 말한다. AspectJ와 같은 자바 언어를
확장한 AOP에서는 마치 자바의 클래스처럼 Aspect를 코드로 작성할 수 있다.
다음은 모든 클래스의 main 메소드 실행(pointcut main()) 후에 "Hello from AspectJ"라는 문자열을 남기는
(after returning advice) Aspect HelloFromAspectJ의 일부이다.
public aspect HelloFromAspectJ{
// define pointcut
pointcut main(): execution(public static void main(String[]));
// define advice
after() returning : main() {
System.out.println("Hello from AspectJ!");
}
}
Aspect 정의에 대한 자세한 설명은 매뉴얼 Spring >> AOP 하위의
Annotation based AOP
,
XML based AOP
,
AspectJ based AOP
를 참고하도록 한다.