다음에서는 AOP 대표적인 툴 중 AspectJ를 이용하여 Aspect를 정의하고 테스트하는 방법에
대해서 다루고자 한다.
Advice 정의
다음에서는 AspectJ 기반에서 동작 시점별 Advice를 정의하는 방법에 대해 살펴보기로 한다.
Before Advice
before()를 이용하여 Before Advice를 정의한다.
다음은
PrintStringUsingAspctJ
의
Before Advice 정의 부분이다. Before Advice는 앞서 정의한 getMethods()라는 Pointcut 전에
"Before Advice of PrintStringUsingAspctJ"라는 문자열과 해당 Pointcut을 가진 클래스명,
메소드명을 출력하는 역할을 수행한다.
before() : getMethods() {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out.println("Before Advice of PrintStringUsingAspctJ");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
위에서 제시한 Before Advice는 내부에 정의된 JoinPoint 유형의 thisJoinPoint라는 객체를 이용하여,
Target 클래스명, 메소드명 등과 같은 Target 정보를 추출하고 있다.
AfterReturning Advice
after() returning()을 이용하여 AfterReturning Advice를 정의한다.
다음은
PrintStringUsingAspctJ
의
AfterReturning Advice 정의 부분으로 해당 Pointcut 실행 결과를 retVal이라는 변수에 담도록 정의하고 있다.
AfterReturning Advice는 앞서 정의한 Pointcut 후에 , "AfterReturning Advice of PrintStringUsingAspctJ"라는
문자열과 해당 Pointcut을 가진 클래스명, 메소드명을 출력하는 역할을 수행한다.
after() returning(UserVO retVal) : getMethods() {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out
.println("AfterReturning Advice of PrintStringUsingAspctJ");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
위에서 제시한 AfterReturning Advice는 내부 정의된 JoinPoint 유형의 thisJoinPoint라는 객체를 이용하여,
Target 클래스명, 메소드명 등과 같은 Target 정보를 추출하고 있다. 또한, 1개의 입력 인자(UserVO)를 가지고 있는데
이는 해당 Pointcut의 실행 결과이다. AfterReturning Advice에서 특정 Pointcut 실행 결과를 참조해야 한다면, Advice 정의시
returning에 해당하는 객체를 정의하고 메소드 로직 내에서 이를 활용하면 된다. 입력 인자는 AfterReturning Advice 정의시
필요에 따라 선택 정의할 수 있다.
AfterThrowing Advice
after() throwing()을 이용하여 AfterThrowing Advice를 정의한다.
다음은
PrintStringUsingAspctJ
의
AfterThrowing Advice 정의 부분으로 해당 Pointcut 실행시 발생한 Exception 객체를 exception이라는 변수에 담도록 정의하고 있다.
AfterThrowing Advice는 앞서 정의한 Pointcut에서 Exception이 발생한 후에 , "AfterThrowing Advice of PrintStringUsingAspctJ"라는 문자열과
해당 Pointcut을 가진 클래스명, 메소드명을 출력하는 역할을 수행한다.
after() throwing(Exception exception) : getMethods() {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out
.println("AfterThrowing Advice of PrintStringUsingAspctJ");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
위에서 제시한 AfterThrowing Advice는 내부 정의된 JoinPoint 유형의 thisJoinPoint라는 객체를 이용하여,
Target 클래스명, 메소드명 등과 같은 Target 정보를 추출하고 있다. 또한, 1개의 입력 인자(Exception)를 가지고 있는데
이것은 Pointcut 실행시 발생한 Exception 객체이다. AfterThrowing Advice에서 특정 Pointcut 실행시 발생한 Exception을
참조해야 한다면, Advice 정의시 throwing에 해당하는 객체를 메소드 로직 내에서 이를 활용하면 된다. 입력 인자는 AfterThrowing Advice
정의시 필요에 따라 선택 정의할 수 있다.
After(finally) Advice
after()를 이용하여 After(finally) Advice를 정의한다.
다음은
PrintStringUsingAspctJ
의
After(finally) Advice 정의 부분이다. After(finally) Advice는 앞서 정의한
getMethods()라는 Pointcut 후에 "After(finally) Advice of PrintStringUsingAspctJ"라는
문자열과 해당 Pointcut을 가진 클래스명, 메소드명을 출력하는 역할을 수행한다.
after() : getMethods() {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out
.println("After(finally) Advice of PrintStringUsingAspctJ");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
위에서 제시한 After(finally) Advice는 내부에 정의된 JoinPoint 유형의 thisJoinPoint라는 객체를 이용하여,
Target 클래스명, 메소드명 등과 같은 Target 정보를 추출하고 있다.
Around Advice
around()를 이용하여 Around Advice를 정의한다.
다음은
PrintStringAroundUsingAspctJ
의
Around Advice 정의 부분으로 다른 Advice와 다르게 Return Type 정의가 추가되어 있음을 알 수 있다.
Around Advice는 updateMethods()라는 Pointcut 후에 "Around Advice of PrintStringUsingAnnotation"라는 문자열과 해당 Pointcut을 가진
클래스명, 메소드명을 출력하는 역할을 수행한다.
Object around() : updateMethods() {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out.println("Around Advice of PrintStringUsingAspctJ");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
// before logic
Object retVal = proceed();
// after logic
return retVal;
}
위에서 제시한 Around Advice는 내부에 정의된 JoinPoint 유형의 thisJoinPoint라는 객체를 이용하여,
Target 클래스명, 메소드명 등과 같은 Target 정보를 추출하고 있다.
또한 Around Advice 내에서 proceed()라는 메소드 호출을 통해 대상 Pointcut을 실행할 수 있어,
Pointcut 전, 후 처리가 가능하며, Pointcut 실행 시점을 결정할 수 있게 된다. 또한 다른 Advice와는
달리 입력값, target, return 값 등에 대해 변경이 가능하다.
Aspect 실행
이제 테스트코드
PrintStringAspectUsingAspctJTest
를
이용하여 앞서 언급한 Aspect들이 정상적으로 동작하는지 확인해 보도록 하자.
다음은 테스트코드
PrintStringAspectUsingAspctJTest
의
testGetUser() 메소드로 integration.anyframe.services 패키지에 속한 UserServiceImpl 클래스의 getXXX() 메소드를 호출하는 로직으로
구성되어 있다.
public void testGetUser() throws Exception {
UserService userService = (UserService) context.getBean(UserService.ROLE);
String userID = "woos41";
// 1. get user
userService.getUser(userID);
// 2. get user with exception
try {
userService.getUserWithException();
} catch (EmpException e) {
// ignore
}
}
첫번째 로직 userService.getUser(userID); 실행시
Before, AfterReturning, After Advice가 적용되며,
콘솔창에 다음과 같은 실행 결과를 포함하게 된다.
Before Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.getUser()***
AfterReturning Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.getUser()***
After(finally) Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.getUser()***
또한 두번째 로직 userService.getUserWithException(); 실행시 테스트를 위해 getUserWithException() 메소드 내에서
EmpException을 throw 하고 있으므로,
Before, AfterThrowing, After Advice가 적용되어 콘솔창에 다음과 같은 실행
결과를 포함하게 된다.
Before Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.getUserWithException()***
...중략...
AfterThrowing Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.getUserWithException()***
After(finally) Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.getUserWithException()***
다음은 테스트코드
PrintStringAspectUsingAspctJTest
의
testUpdateUser() 메소드로 integration.anyframe.services 패키지에 속한 UserServiceImpl 클래스의 updateXXX() 메소드를 호출하는 로직으로
구성되어 있다.
public void testUpdateUser() throws EmpException {
UserService userService = (UserService) context.getBean(UserService.ROLE);
UserVO userVO = new UserVO();
userVO.setUserId("woos41");
userVO.setUserName("updgang");
userVO.setCellPhone("9876543210");
userVO.setAddr("kamala road --");
// 1. update user
userService.updateUser(userVO);
}
userService.updateUser(userID); 실행시
Around Advice가 적용되며, 콘솔창에 다음과 같은 실행 결과를 포함하게 된다.
Around Advice of PrintStringUsingAspctJ
***class integration.anyframe.services.aop.service.UserServiceImpl.updateUser()***