관점지향 프로그램에 관한 글을 얻었습니다.
다소 오래 된 글인것 같기도 합니다.
Aspect-Oriented Programming with AspectJ
횡단-관심사(crosscutting-concerns)는 애플리케션에서 특히 흥미를 주는 영역이며 그 구현이 여러 모듈에 영향을 미칠 수 있는 시스템 전역적 문제이다.
횡단-관심사를 판단하는 척도는 다음과 같다.
? 엉킨 코드 : 코드 엉킴은 모듈이 한번에 여러 문제를 다룰 때 일어난다. 이에 대한 좋은 예는 여러 가지 요구 사항을 동시에 처리하는 뱅킹(banking) 시스템 모듈이다. 이 모듈은 비즈니스 로직, 성능, 로깅(logging), 트랜잭션 관리 그리고 보안을 동시에 처리한다. 각각의 문제를 해결하는 많은 엘리먼트를 시스템에 추가하면 코드가 굉장히 복잡해진다.
? 흩어진 코드 : 코드 흩어짐은 횡단-관심사에 대한 구현이 여러 모듈에 걸쳐있을 경우 발생한다. 여기에는 두 종류의 코드 흩어짐이 있다. 첫 째는 코드 중복이며 이는 코드의 복잡도를 증가 시킨다. 예를 들어?고전적인 트랜잭션 관리는 애플리케이션의 여러 모듈에 거의 똑 같은 코드를 넣도록 요구한다.
두 번째 유형의 코드 흩어짐은 어떤 문제를 보완하기 위한 구현이 여러 모듈에 걸쳐서 느슨하게 분산되어 있을 경우 일어난다. 예를 들어, 캐싱에 대한 공통 구현에서, 한 모듈이 유효하지 않은 데이터가 저장되는 것을 막기 위해 캐시를 무효화하는 동안 다른 모듈은 캐시로부터 객체를 조회하는 경우 문제가 발생하게 된다.
Example ? Caching as a Crosscutting Concern
이 기사에서 캐싱을 횡단-관심사 구현의 예로써 사용한다. 우리의 목표는 다음과 같다.데이터소스에 고객 아이디를 주고 고객을 조회하는 고객 관리자 구현 ? 그러나 명시적인 캐싱 코드를 포함하지는 않는다.
|
public class CustomerManager() { private CustomerDao customerDao; public Customer getCustomer(String customerId) { return this.customerDao.getCustomer(customerId); } // rest of class implementation } |
표1. 고객 관리자를 간단히 구현한 예
데이터소스로부터 고객을 조회하는 것이 굉장히 비싼 연산이라 가정하자. 이럴 경우, 애플리케이션의 성능 향상을 위한 한 방법은 조회된 고객을 캐시에 저장하는 것이다. 즉, CustomerDao.getCustomer(String) 메소드가 반환하는 값을 캐시에 넣는 것이다.
|
public class CustomerManager() { private static final Object NULL_OBJECT = new Object(); private CacheManager cacheManager; private CustomerDao customerDao; public Customer getCustomer(String customerId) { String key = “customer.” + customerId; Customer customer = null; Object cachedEntry = null; try { cachedEntry = this.cacheManager.get(key); } catch (CacheException e) { logCacheException(e); } if (cachedEntry == null) { customer = this.customerDao.getCustomer(customerId); Object objectToCache = customer; if (objectToCache == null) { objectToCache = NULL_OBJECT; } try { this.cacheManager.put(key, objectToCache); } catch (CacheException e) { logCacheException(e); } } else if (cachedEntry != NULL_OBJECT) { customer = (Customer) cachedEntry; } return customer; } // rest of class implementation } |
표2. OOP 기반, 애스펙에 기반하지 않은 캐싱 구현
엉킨 코드와 흩어진 코드는 소프트웨어에 다음과 같은 부정적인 영향을 준다.:
? 복잡성 코드는 점점 이해하기 힘들어지며 주된 관심사가 뭔지 알기도 힘들어진다. 우리 예에서 보면, 굵은 글씨체로 표시된 부분만이 메소드가 진짜 수행하는 작업이다.
? 유지보수 코드 유지보수가 점점 어려워진다. 관심사(concern)에 대한 구현을 포함하고 있는 유지 보수 작업은 관련 없는 다른 부분에 영향을 주기도 한다. 이와 마찬가지로, 여러 개의 관심사를 구현한 모듈이 한가지 관심사를 구현한 모듈보다 더 빈번히 유지 보수되는 경향이 있다.[4]
? 테스트 용이성 코드를 테스트하기 점점 힘들어진다. 예제에서 사용한 메소드의 비즈니스 로직을 테스트하기 위해서는 캐시 관리자가 필요하거나 적어도 시뮬레이션을 위한 가짜 객체(mock object)가 필요하다.
? 코드 재사용 재사용하기 힘든 코드가 된다. 여러 개의 문제를 다루고 있는 모듈을 그것과 다른 구현을 요구하는 시스템에서 혹은 완전히 다른 문제를 다루는 시스템에서 재사용하기는 어렵다.(불가능할 수도 있다.) 예를 들어, 고객 정보를 조회하길 원하지만 다른 캐싱 방법이 필요하거나 캐싱이 전혀 필요 없는 시스템에서는 CustomerManager 클래스를 재사용 할 수 없다.
표2의 코드를 리팩토링해서 코드 엉킴과 흩어짐을 최소화 해본다. 이를 위해 상세 구현을 숨기고 추상화된 API를 제공하는 캐싱 모듈을 만들었다. 이는 그림-1과 같다. 캐시관리자가 한 모듈로 만들어 지고 이 모듈로 호출이 집중되기 때문에 중복된 코드가 다소 줄어든다. 그러나 형태만 약간 달라 졌을 뿐 풀어야 할 문제가 여전히 남아있다. 새로운 캐싱 API 호출이 클라이언트 모듈과 엉켜있고 시스템의 여러 부분에 흩어져있다.
|
그림-1 |
구원 투수 AspectJ(AspectJ to the Rescue)
AOP는 횡단-관심사의 분리를 통해 이 문제를 해결해준다. AOP는 개개의 관심사를 해결해주는 구현을 독립된 모듈로 만들어 프로그램을 구조화하는 방법을 제공한다. AOP는 객체 모듈 주변에 혼란스럽게 흩어져 있는 코드 대신 횡단-관심사를 애스펙aspect 이라는 새로운 작업 유닛으로 모듈화 해준다. AOP의 가장 완벽한 구현인 AspectJ는 자바 언어의 확장이며 자바는 AOP 개념을 자바의 일급 시민(first-class citizen)으로 다루고 있다.[2]
모듈화가 애스펙aspect이 주는 유일한 이익은 아니다. 애스펙Aspect은 횡단-관심사에 대한 세부 구현을 캡슐화한다. 클래스와는 다르게, 액스펙은 방법과 시점을 모두 감춰주며 횡단-관심사를 모듈화하고 캡슐화함으로써 문제에 대한 추상화 단위(unit of abstraction)가 된다. 추상화, 모듈화 그리고 캡슐화는 AOP를 소프트웨어 복잡성을 다루고, OOP의 약점(횡단 문제를 해결하기 힘듬)을 보완해 주는 강력한 도구로 만들어준다.
그림-2는 캐싱에 대한 문제가 액스펙으로 모듈화된 것을 보여준다. 핵심 모듈(온라인 뱅킹 모듈과 휴먼 리소스 모듈)은 캥싱 API에 대한 어떤 호출도 포함하고 있지 않다. 사실 핵심 모듈은 캐싱을 사용하고 있는지도 모른다.
|
그림-2 |
|
public aspect CachingAspect() { private static final Object NULL_OBJECT = new Object(); private CacheManager cacheManager; pointcut getCustomerOperation(String customerId) : execution(public Customer CustomerManager.getCustomer(String)) && args(customerId); Customer around(String customerId) : getCustomerOperation(customerId) { String key = “customer.” + customerId; Customer customer = null; Object cachedEntry = null; try { cachedEntry = this.cacheManager.get(key); } catch (CacheException e) { logCacheException(e); } if (cachedEntry == null) { customer = proceed(customerId); Object objectToCache = customer; if (objectToCache == null) { objectToCache = NULL_OBJECT; } try { this.cacheManager.put(key, objectToCache); } catch (CacheException e) { logCacheException(e); } } else if (cachedEntry != NULL_OBJECT) { customer = (Customer) cachedEntry; } return customer; } // rest of aspect implementation } |
코드에서 볼 수 있는 것처럼, 캐싱 애스팩은 코드 엉킴과 흩어짐 문제를 효과적으로 해결한다. 그러나 이런 접근법은 다음과 같은 새로운 이슈를 제기한다 ? 핵심 모듈이 캐싱에 대한 알지 못한 다면, 언제, 어디서 캐싱을 실행해야 할지 시스템이 어떻게 알 수 있을까?
조립(Weaving)
이 질문에 대한 답은 조립-법칙(weaving rules)이라는 AOP 개념에서 찾을 수 있다. 조립-법칙은 특정 관심사에 대한 구현이 최종 시스템 빌드와 어떻게 통합되는지를 설명한다. 조립-법칙은 프로그램 실행 시 캐싱이 어디서 수행돼야 하는지, 캐싱에 저장되는 정보가 무엇인지 등을 식별한다.시스템은 특정 연산에서 캐싱 API를 적절히 호출하기 위해 이런 규칙을 사용한다. 조립-법칙을 통해 관심사에 대한 구현을 최종 시스템에 통합하는 과정을 조립(weaving)이라 부른다.
AspectJ의 조립-법칙은 3가지 개념에 기반을 둔다.
? Join points. 조인-포인트는 프로그램이 실행되는 동안에 발생하는 특정 시점을 나타냄
예) 매소드 수행이나 필드 접근 시점
? Advice. 어드바이스는 특정 조인-포인트에서 취해져야 할 활동action 을 나타냄. 예), 캐싱을 실행하는 로직
? Pointcut. 포인트-컷은 언제 어드바이스가 실행되야 하는지를 상세히 설명하는 조인포인트의 집합을 표현한다. 예) 표2에 getCustomer 메소드에 정의된 조인포인트를 식별하는 포인트-컷
조립(엮음)은 컴파일 시점, 컴파일 이후(존재하는 클래스나 jar 파일과 조립을 위해) 또는 로드시점(클래스 로드)에서 일어난다. ? 이와 관련된 내용은 기사의 논점을 벗어나기 때문에 더 이상 언급하지 않는다.
조인-포인트(Join points)
AspectJ은 한 프로그램 안에서 여러 다른 유형의 조인-포인트를 사용할 수 있다.
? 메소드 호출과 실행
? 생성자 호출과 실행
? 필드에 읽기/쓰기 접근
? 클래스와 객체 초기화
? 예외 처리 실행
AspectJ는 이런 지점들에서 횡단-관심사를 구현한 추가 코드와 조립할 수 있다. 그림-3에서는 CustomerManager클래스에 createCustomer 메소드 실행 동안 사용 가능한 다양한 조인-포인트를 보여준다.
|
그림-3 |
Aspects, Pointcuts and Advice
애스펙(Aspects)
클래스가 자바의 핵심 작업 단위인 것과 마찬가지로 애스팩은 AspectJ에 핵심 작업 단위이다. 한 액스팩Aspect은 포인트-컷, 어드바이스, 그리고 선언으로 구성된다. 이런 엘리먼트에 추가적으로, 액스팩Aspect은 기존의 자바 클래스 같이 데이터, 메소드, 중첩 클래스를 포함할 수 있다. 일반적인 형태의 애스팩은 다음과 같다.
|
[access-modifier] aspect name [extends class-or-aspect] [implements interface-list] { …aspect body } |
다음은 표3의 캐싱 에스팩의 정이다.
|
public aspect CachingAspect { // aspect body } |
애스팩Aspec은 상당히 복잡한 구현물이다. – 추상abstract 형식으로 선언(상속도 가능)될 수 있으며 특정 인터페이스를 구현하는 형식으로 선언될 수도 있다. ? 그러나 이기사의 핵심은 애스팩을 애플리케이션이 해결해야 하는 특정 횡단-관심사(캐싱문제 같은)를 캡슐화하는 엘리먼트로 사용힐 수 있다는 것이다. 그러나 클래스와는 달리 액스팩은 독립적으로 인스턴스화 할 수 없다는 것을 명심해야 한다. AspectJ 컴파일러는 애스팩Aspec을 애플리케이션의 다른 부분의 행위와 조립하기 위해 사용한다.
포인트컷(Pointcuts)
포인트-컷은 프로그램-흐름 안에 특정 조인-포인트를 식별하기 위해 사용된다. 이 조인-포인트에서 프로그램과 추가적인 행위(어드바이advice)가 엮어진다. 포인트-컷은 에스팩Aspec, 클래스, 인터페이스 내부에 선언될 수 있다. 다른 자바 구조물artifacts처럼, 포인트-컷은 포인트-컷에 대한 접근을 제한하기 위해 접근 제어자(access modifiers)를 가질 수 있다.(public, protected, private 또는 디폴트)
AspectJ에서 명명된named 포인트-컷 정의의 일반적인 형식은 다음과 같다.
|
[access-modifier] pointcut name([parameter-list]) : pointcut-expression; |
다음은 표3에서 사용한 명명된 포인트-컷의 정의이다.
|
pointcut getCustomerOperation(String customerId) : execution(public Customer CustomerManager.getCustomer(String)) && args(customerId); |
포인트-컷을 익명으로 사용할 수도 있다. ? 명시적인 이름 없이 ? 그러나 대부분 이름을 갖는다. AspectJ는 포인트-컷을 만드는데 사용할 수 있는 다양한 형식의 패턴을 제공한다.(문자 패턴 매칭 형식) ? 앞 부분에서 봤던 다양한 유형의 조인-포인트 매칭을 가능케 해주기 위해. 다음과 같은 패턴들이 존재하다.
type-signature patterns, method and constructor signature patterns, field signature patterns,
lexical-flow based pointcuts, control-flow based pointcuts, argument pointcuts, execution object pointcuts(휴~)
|
pointcut allGettersExceptCustomerOperations() : getterOperations() && !(* CustomerManager.get*(..)) |
AspectJ는 또한 상당히 넓은(좁은) 범위의 매칭matching을 제공하기 위한 풍부한 연산자와 와일드카드를 제공한다.
|
와일드카드 |
정의 |
|
* |
공백을 제외한 임의의 수의 문자와 매칭 |
|
.. |
공백을 포함하는 임의의 수의 문자와 매칭 |
|
+ |
특정 유형의 서브-클래스, 서브-인터페이스와 매칭 |
어드바이스(Advice)
일단 조인-포인트를 포인트-컷으로 표현했다면, 추가할 행위에 대한 명세가 필요하다. ? 이것을 어드바이스advice라고 부른다. 어드바이스advice는 다음과 같은 3가지 종류가 있다 :
? Before advice: 조인–포인트가 실행되기 전에 처리할 행위를 명세.
? After advice: 조인-포인트가 실행된 이후에 처리할 행위를 명세
? Around advice: 조인-포인트 실행에 걸쳐서 처리할 행위를 명세. ? 어드바이스advice는 자신의 작업을 수행하는 것 외에도 조인-포인트 실행에 대한 제어와 책임을 갖는다.[2]
![]() |
|
그림-4. |
모든 어드바이스 선언은 똑같은 기본 구조를 사용한다.[4]
|
[strictfp] advice-specification [throws type-list] : pointcut-expression { …body of advice } |
어드바이스advice는 명시적으로 호출할 수 없다. 그러므로 접근-제어자가 필요 없으며 strictfp 제어자만이 사용된다. Strictfp는 어드바이스 구현 안에 모든 부동소수점 연산을 FT-strict형식[4]으로 만들어준다.(역자 주: 자바 고유 키워드 입니다. 저도 이번에 첨으로 알았다는…) throw 절은 어드바이스advice가 던질 수 있는 예외를 명세한다. 어드바이스advice는 조인-포인트를 호출하는 클라이언트가 예상하지 못하는 예외(checked-exception)을 던질 수 없다.
3가지 유형의 어드바이스에 대해 더 자세히 살펴본다.
Before Advice
Before 어드바이스advice는 특정 조인-포인트가 수행되기 전에 실행된다. 따라서 그림-4에서 보면 CustomerDa클래스의 getCustomer메소드가 호출되기 전에 어드바이스가 실행된다.
|
before() : call (* CustomerDao.get*(..)) { // advice body } |
Before 어드바이스advice에서 예외가 발생하면, 대상이 되는 조인-포인트 연산은 실행되지 않는다.[3]
After advice
After 어드바이스advice는 특정 조인-포인트가 수행된 이후에 실행된다. AspectJ는 3종류의 After 어드바이스를 제공한다.[2] :
After returning advice(호출이 예외exception 없이 성공적으로 완료된 후에 실행됨)은 다음과 같은 형식을 갖는다.
|
after() returning : call (* CustomerDao.get*(..)) { // advice body } |
After thowing advice. 조인-포인트에서 특수한 예외exception 가 발생하면 실행된다.
|
after() throwing : call (* CustomerDao.get*(..)) { // advice body } |
After advice 에러 발생 여부에 상관 없이 조인-포인트가 호출된 이후에 무조건 실행된다.
|
after(): call (* CustomerDao.get*(..)) { // advice body } |
Around advice
around 어드바이스advice는 조인 포인트를 감싼다. 어드바이스advice 중에 가장 강력하며 그 이유는 다음과 같다.:
? 조인?포인트?실행을?건너?뛸?수?있다.?
? 동일한(동일하지 않은) 아규먼트로?조인?포인트를?실행할?수?있다.
? 조인?포인트를?여러?번?실행할?수?있다. 매번?다른?아규먼트로?조인?포인트?실행?가능
포인트-컷 안에서 연산을 실행하기 위해서는 어드바이스 구현 부에서 proceed() 키워드를 사용해야 한다.[3] proceed()를 호출하지 않으면 조인-포인트는 실행되지 않는다. 그리고 대상 오퍼레이션이 필요로 하는 것과 동일한 수의 파리미터와 동일한 타입의 파리미터를 포함해야 한다. 마찬가지로, proceed()는 대상 연산에 의해서 반환되는 것과 동일한 값을 반환한다.
다음은 목록3에서 around 어드바이스 사용 부분만 발췌한 것이다.
|
… Customer around(String customerId) : getCustomerOperation(customerId) { String key = “customer.” + customerId; Customer customer = null; Object cachedEntry = null; try { cachedEntry = this.cacheManager.get(key); } catch (CacheException e) { logCacheException(e); } if (cachedEntry == null) { customer = proceed(customerId); Object objectToCache = customer; if (objectToCache == null) { objectToCache = NULL_OBJECT; } try { this.cacheManager.put(key, objectToCache); } catch (CacheException e) { logCacheException(e); } } else if (cachedEntry != NULL_OBJECT) { customer = (Customer) cachedEntry; } return customer; } // rest of aspect implementation } |
표3의 코드가 완벽하지는 않지만, 횡단-관심사가 Customer 클래스로부터 제거되고 캐싱 에스펙(Caching Aspect)이라는 독립된 모듈로 구현되었다는 것을 잘 보여준다.
상호–타입 선언(Inter-type declarations)
주제를 약간 바꿔서 이제 상호-타입 선언(inter-type declaration)에 관련된 AspectJ의 또 다른 기능을 살펴보자. (공식적으로 도입(introductions)으로 알려짐) 상호-타입 선언은 클래스간, 클래스 계층간을 넘어서는 선언이다.[7] 상호-타입 선언은 타입간(클래스,인퍼페이스,애스펙)의 정적인 구조와 컴파일 시점의 행위를 변경한다. 다시 말해, 상호-타입 선언은 횡단-관심사를 독립된 모듈 단위로 분리하는 것을 돕는다.
상호-타입 선언은 존재하는 클래스에 필드, 메소드, 생성자를 도입하기 위해 사용할 수 있다. 물론 인터페이스나 상속 계층을 잘 통제되는 방법(controlled manner)으로 다룰 수도 있다.
필드 도입(Field introduction)
필드 도입은 존재하는 클래스에 새로운 속성을 추가하기 위해 사용된다. ? 그러나 기존 클래스로부터 새로운 코드(다른 관심사)를 분리할 목적으로 사용. 상호-타입 필드의 일반적인 형태는 다음과 같다.[4]
[modifiers] field-type target-type.field-name;
다음 예에서, Customer 클래스의 프로퍼티에 변경이 일어날 경우 이를 “기록”할 수 있는 listeners 필드를 도입한다.
private List<PropertyChangeListeners> Customer.listeners =
new ArrayList<PropertyChangeListeners>();
리스너 필드는 customer의 상태와는 관련이 없다. 그러나 이 필드의 선언과 사용은 Customer 클래스의 구현에 흩어져있다. 더욱이, 프로퍼티의 변경을 기록하는 것은 Customer 클래스의 책임이 아니다.
메소드와 생성자 도입(Method and constructor introduction)
인터-타입 메소드의 일반적인 형태는 다음과 같다:[4]
|
[modifiers] return-type target-type.method-name([parameter-list]) [throws type-list] { method-body } |
그리고 다음은 인터-타입 생성자이다.
|
[modifiers] target-type.new([parameter-list]) [throws type-list] { method-body } |
상호-타입 필드와 같이, 상호-타입 메소드는 public, private 또는 디폴트 접근-제어자를 가질 수 있다. 그러나 protected 접근-제어자는 사용할 수 없다. 상호-타입 메소드의 선언은 target-type을 추가하는 것을 제외하고 일반적인 메소드 선언과 비슷하다.
다음은 앞에서 소개한 예제 리스너에 인터-타입 생성자 등록을 추가한 것이다.
|
public Customer.new(String customerId) { this.customerId = customerId; } private List<PropertyChangeListeners> Customer.listeners = new ArrayList<PropertyChangeListeners>(); public void Customer.addListener(PropertyChangeListener listener) { listeners.add(listener); } public Listener Customer.removeListener(int index) { return listener.remove(index); } |
위에서 보는 것과 같이, Customer 클래스에 일반적인 필드를 참조하는 것과 같은 방법으로 인터-타입 필드 listeners를 참조할 수 있다. 도입된 메소드가 public이기 때문에 애플리케이션의 어디서도 호출할 수 있다.
인터페이스와 상위타입 변경(Interface and super type manipulation)
여러 개의 고객 인스턴스를 목록에 저장해야 한다고 가정하자. 그리고 나중에, 고객 목록을 내림차순(고객의 성을 이용하여)으로 정렬하여 웹 페이지에 표시해야 한다는 것을 알았다.
이것을 해결하기 위한 한 방법은 Customer클래스가 Comparable인터페이스를 구현하는 것이다. 만약 이 방법을 선택한다면 compareTo 메소드도 구현해야 한다. 이 추가 코드는 고객의 상태와는 관련이 없다. 따라서 이 기능을 고객 클래스의 구현으로부터 분리하기를 원한다. 다음은 AspectJ를 통해서 어떻게 이것을 구현할 수 있는 지를 보여준다.
|
declare parents : Customer implements Comparable; public int Customer.compareTo(Object obj) { // method implementation } |
|
declare parents : Customer extends Observable; |
그리고 마지막으로 AspectJ 어노테이션
AspectJ 5에서는 POJO를 사용하여 애스펙을 작성하는 것이 가능하다. AspectJ는 보통의 자바 클래스를 애스펙으로 인식하도록 하기 위해 Java5의 어노테이션을 사용한다. 자바 클래스는 조립기(weaver)에 의해 애스펙으로 번역된다. AspectJ 어노테이션은 org.aspectj.lang.annotation package.에 있다.
다음은 자바를 사용하여 재작성된 AspectJ 엘리먼트에 대한 예이다. :
|
AspectJ 언어 |
AspectJ 어노테이션을 사용시 |
|
public aspect MyAspect { // aspect body } |
@Aspect public class MyAspect { // aspect body } |
|
pointcut aCall() : call(* *.*(..)); |
@Pointcut(“call(* *.*(..))”) void aCall() {} |
|
before() : aCall() { // advice body } |
@Before(“aCall()”) void aCallAdvice() { // advice body } |
표3에 애스펙을 POJO를 사용해서 작성하면 다음과 같다.
|
@Aspect public class CachingAspect() { private static final Object NULL_OBJECT = new Object(); private CacheManager cacheManager; @Pointcut(“execution(” + “public Customer CustomerManager.getCustomer(String)) ” + “&& args(customerId)”) void getCustomerOperation(String customerId) { } @Around(“getCustomerOperation(customerId)”) public Customer onCustomerOperation(ProceedingJoinPoint thisJoinPoint,String CustomerId) { String key = “customer.” + customerId; Customer customer = null; Object cachedEntry = null; try { cachedEntry = this.cacheManager.get(key); } catch (CacheException e) { logCacheException(e); } if (cachedEntry == null) { customer = thisJoinPoint.proceed(new Object[] {customerId}); Object objectToCache = customer; if (objectToCache == null) { objectToCache = NULL_OBJECT; } try { this.cacheManager.put(key, objectToCache); } catch (CacheException e) { logCacheException(e); } } else if (cachedEntry != NULL_OBJECT) { customer = (Customer) cachedEntry; } return customer; } // rest of aspect implementation } |
표7
표7에 애스펙은 자바를 사용할 경우 proceed()가 어떻게 구현되는지를 보여준다. 표5에 애스펙처럼 proceed()를 호출하면 메소드가 존재하지 않기 때문에 컴파일 에러가 발생한다. 이를 해결하기 위해서는 around 어드바이스 같이 어노테이션을 갖는 메소드(annotated method)는 아규먼트로 org.aspectj.lang.ProceedingJoinPoint의 인스턴스를 넘겨야 한다. ProceedingJoinPoint는 around 어드바이스를 지원하기 위해서 proceed(..) 메소드를 인터페이스로 갖는다.
POJO를 사용해서 생성된 애스펙은 Java5 컴파일러로 컴파일 된 후 추가적인 빌드 단계에서 AspectJ 조립기weaver에 의해 조립된다. AspectJ 언어 확장을 지원하지 않는 IDE(예, NetBeans)로 작업한다면 애스펙을 POJO로 직접 작성할 수 있다. 한 애플리케이션에서 POJO와 AspectJ 언어를 모두 사용해서 애스펙을 작성할 수 있다.
결론(Conclusions)
애스펙-지향 프로그램은 모듈화, 캡슐화 그리고 횡단-관심사에 추상화에 초점을 맞춘다. AOP는 OOP와 경쟁을 목적으로 하지 않는다. 대신 AOP는 횡단 관심사에 대한 구현 같은 OOP의 약점을 보완한다.
AspectJ는 AOP의 가장 완벽한 구현을 제공한다. AspectJ는 횡단-관심사 구현에 대한 필수적인 엘리먼트를 정의하는 자바 언어에 대한 확장을 제공한다. AspectJ가 자바의 확장이기 때문에, 개발자는 AspectJ를 쉽게 배울 수 있다. 이 기사에서, 우리는 AspectJ로 할 수 있는 것에 대해 개략적으로 살펴봤다. 로드시점 조립(load-time weaving)과 POJO로 애스펙 작성 같은 버전 5에서 추가된 새로운 기능은 AspectJ를 소프트웨어 복잡성에 대응하는 가장 강력한 툴로 만들어주고 있다.
References
[1] E. Gamma et al, “Design Patterns: Elements of Reusable Object-Oriented Software”, Addison-Wesley, 1995
[2] R. Johnson and J. Hoeller, “J2EE Development Without EJB”, Wrox Press, 2004
[3] R. Laddad, “AspectJ in Action: Practical Aspect-Oriented Programming”, Manning, 2003
[4] A. Colyer et al, “eclipse AspectJ”, Addison-Wesley, 2005
[5]
[6] Wikipedia at: http://en.wikipedia.org/wiki/Aspect-oriented_programming (last visited, January 2006)
[7] The AspectJ Programming Guide at: http://www.eclipse.org/aspectj/doc/released/progguide/index.html (last visited, January 2006)
[8] S. Clarke and E. Baniassad, “Aspect-Oriented Analysis and Design. The Theme Approach”, Addison-Wesley, 2005
“ObjectView”라는 온라인 잡지에 난 기사인데요…
AspectJ에 대한 기본적인 내용들이 잘 정리되어 있어서 한번 옮겨봤습니다.
번역은 제 맘대로 했으니 번역의 질에 대한 딴지는 사절입니다. ^^;
참고로 고유명사에 대한 번역은 다른 책들을 참고하거나 그대로 썼습니다.
예를 들어 crosscutting-concerns 같은 경우 모책에서 ‘횡단-관심사’라고 번역을 했길래 그대로 따랐습니다.
———————————————————————



