AspectJ 재미있군요 ㅎㅎ;
간단한 애플리케이션을 작성하여 AspectJ를 이해해 보자.
애플리케이션에 포함된 코드를 혹시 전부 다 이해하지 못하여도 기본적인 개념은 잡을 수 있을 것이다. 여기에 나오는 예는 AspectJ in Action(http://greenpress.co.kr/list/list_view.asp?sku=301)에 있는 2.2절에서 발췌한 것이다. 이 블로그에 있는 기사 Aspect Oriented Programming(AOP) 를 먼저 읽으면 이 코드를 이해하기 쉬울 것이다.
예제 1에 있는 것처럼, 메시지를 프린트하는 두 개의 메서드가 포함된 클래스를 생성하자.
예제 1 MessageCommunicator.java
public class MessageCommunicator {
public static void deliver(String message) {
System.out.println(message);
}
public static void deliver(String person, String message) {
System.out.print(person + ", " + message);
}
}
MessageCommunicator 클래스는 메서드 두 개(일반인에게 메시지를 보내는 메서드와 특정인에게 메시지를 보내는 메서드)를 가지고 있다. 다음에 MessageCommunicator 클래스의 기능을 이용하는 단순한 클래스를 예제 2에 작성하자.
예제 2 Test.java
public class Test {
public static void main(String[] args) {
MessageCommunicator.deliver("AspectJ를 배우고 싶으세요?");
MessageCommunicator.deliver("길동", "재미있어요?");
}
}
MessageCommunicator와 Test 클래스를 함께 컴파일하고 Test 프로그램을 실행하면 다음과 같은 출력을 얻는다. 모든 유효한 Java 프로그램은 유효한 AspectJ 프로그램이므로 javac와 같은 Java 컴파일러 대신에 AspectJ 컴파일러(ajc)를 사용하여 동일한 결과를 얻을 수 있다.
>ajc MessageCommunicator.java Test.java
>java Test
AspectJ를 배우고 싶으세요?
길동, 재미있어요?
MessageCommunicator 클래스에 있는 코드를 한 줄도 변경하지 않고 애스펙트를 추가함으로 클래스의 기능을 향상시킬 수 있다. 예제 3에 있는 것처럼 예의범절 애스펙트(횡단 관심사)를 구현하자. 이 애스펙트는 “안녕하세요!"로 먼저 인사하고 그 후에 모든 메시지를 전달한다.
예제 3 MannersAspect.java
public aspect MannersAspect { // ① 애스펙트 선언
pointcut deliverMessage() // ② 교차점 선언
: call(* MessageCommunicator.deliver(..));
before() : deliverMessage() { // ③ 충고
System.out.print("안녕하세요! ");
}
}
이제 클래스 파일을 애스펙트와 함께 컴파일하자. AspectJ 컴파일러인 ajc가 에제 3에 있는 애스펙트를 직조(weaving)한(애스펙트의 기능이 클래스 파일에 삽입되는) 클래스 파일을 생성하려면 모든 입력 파일을 함께 ajc에 제공하여야 한다. 컴파일러를 실행하면 다음과 같은 출력을 보게 된다.
>ajc MessageCommunicator.java MannersAspect.java Test.java
>java Test
안녕하세요! AspectJ를 배우고 싶으세요?
안녕하세요! 길동, 재미있어요?
이 애스펙트와 ajc가 수행하는 마법을 알아보자. MannersAspect.java 파일은 MannersAspect 애스펙트를 선언한다.
① 애스펙트의 선언은 클래스 선언과 유사하다.
② 애스펙트에 정의된 교차점 deliverMessage()는 MessageCommunicator에 있는 메서드 deliver()에 대한 모든 호출을 포착한다. 교차점에 있는 *는 어떤 반환형을 갖는지 관계없이 deliver() 메서드가 모두 포착됨을 의미하며 또한 괄호 안에 있는 ..는 매개변수의 종류와 개수에 관계없이 deliver() 메서드가 모두 포착됨을 의미한다. ..에 의해 예제 1에 있는 MessageCommunicator 클래스의 중복된(overloaded) 두 개의 deliver() 메서드에 대한 모든 호출을 포착할 수 있다.
③ 이제 충고(advice)를 정의한다. before() 부분은 결합점에 있는 코드가 실행되기 이전에 충고가 실행됨을 의미하는데 지금 예에서는 MessageCommunicator.deliver()가 실행되기 전에 충고가 실행된다. 충고에서 메시지 “안녕하세요!"를 줄을 바꾸지 않고 출력한다.
애스펙트가 시스템에 존재하므로 MessageCommunicator.deliver() 메서드가 호출될 때마다 “안녕하세요!"를 출력하는 충고 코드가 메서드 실행 이전에 실행된다.
조금 발전시켜 다른 애스펙트를 추가하도록 하자. 이번에는 한국말 특성을 살려 인사하는 애스펙트를 만든다. 예제 4에서 보이는 것처럼 사람 이름 뒤에 “님”을 부쳐 인사하고 메시지를 전달한다.
예제 4 KoreanSalutationAspect.java
public aspect KoreanSalutationAspect {
pointcut sayToPerson(String person) //① deliver() 메서드를 포착하는 교차점
: call(* MessageCommunicator.deliver(String, String))
&& args(person, String);
void around(String person) : sayToPerson(person) { //② 충고
proceed(person + "님"); // ③ 충고 몸체
}
}
클래스를 MannersAspect와 KoreanSalutationAspect와 함께 컴파일하고 Test 클래스를 실행하면 다음과 같이 출력이 된다.
>ajc MessageCommunicator.java MannersAspect.java
KoreanSalutationAspect Test.java
>java Test
안녕하세요! AspectJ를 배우고 싶으세요?
안녕하세요! 길동님, 재미있어요?
① 교차점 sayToPerson은 두 개의 매개변수를 취하는 MessageCommunicator.deliver()를 호출하는 모든 결합점을 포착한다. 사람 이름 뒤에 “님"을 붙이려면 매개변수 person에 대한 접근이 필요하다. args() 부분이 그런 역할을 한다. args()의 첫 번째 매개변수인 person은 메서드 deliver()의 첫 번째 매개변수가 person으로 들어오는 것을 지정한다. args()의 두 번째 매개변수 String은 메서드 deliver()의 두 번째 매개변수는 내용은 필요 없고 단지 String 형이라는 것을 지정한다. sayToPerson()의 매개변수 person은 deliver()의 첫 번째 매개변수와 일치하여야 한다.
② 출력에서 사람 이름 뒤에 “님”을 부치려면 deliver() 메서드의 매개변수를 고쳐서 실행해야 한다. before() 충고를 사용하면 deliver()가 실행되기 전에 별도의 코드를 실행하고 deliver()가 정작 실행될 때 매개변수가 변하지 않으므로 before() 충고를 사용할 수 없다. 충고를 받는 메서드의 매개변수를 수정하기 위해 around() 충고를 사용한다. around() 충고는 메서드의 기존 환경을 변경하여 실행할 수 있다.
③ 이 부분이 충고의 몸체이다. AspectJ 키워드인 proceed()는 포착된 결합점을 실행하게 하는 명령이다. 메서드 deliver()의 원래 매개변수 person을 sayToPerson(person)으로 포착하여 proceed() 내에서 “님"을 뒤에 부친 후에 proceed()에 넘겨준다. 결과는 수정된 매개변수를 가지고 MessageCommunicator.deliver()를 실행하게 된다.
No comments:
Post a Comment