다음에서는 AOP 대표적인 툴 중 Spring AOP를 이용하여 XML 스키마 기반에서 Aspect를 정의하고 테스트하는 방법에
대해서 다루고자 한다. Spring 2.x 버전부터 AOP 설정을 위한 aop namespace, XML 스키마가 추가되었다.
Advice 정의 및 구현
다음에서는 XML 기반에서 동작 시점별로 Advice 정의 및 구현 방법에 대해 살펴보기로 한다.
Before Advice
<aop:before>를 이용하여 Before Advice를 정의한다.
다음은
applicationContext-aop-xml.xml
의
Before Advice 정의 부분이다. 앞서 정의한 getMethods라는 pointcut을 참조하고 있으며, 해당 pointcut 전에
printStringAspect라는 Bean의 beforeExecuteGetMethod() 메소드를 호출해야 함을 명시하고 있다.
<aop:before method="beforeExecuteGetMethod" pointcut-ref="getMethods" />
다음은 Before Advice를 구현하고 있는
PrintStringUsingXML
클래스의 일부이다.
Before Advice 역할을 수행하는 beforeExecuteGetMethod()는 앞서 정의한 getMethods라는 Pointcut 전에 "Before Advice of PrintStringUsingXML"라는
문자열과 해당 Pointcut을 가진 클래스명, 메소드명을 출력하는 역할을 수행한다.
public class PrintStringUsingXML {
// ...
public void beforeExecuteGetMethod(JoinPoint thisJoinPoint) {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out.println("Before Advice of PrintStringUsingXML");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
// ...
}
beforeExecuteGetMethod()는 1개의 입력 인자(JoinPoint)를 가지고 있는데
Target 클래스명, 메소드명 등과 같은 Target 정보를 포함하고 있다. Target 정보가 불필요한 Advice인 경우에는
JoinPoint라는 입력 인자를 선언하지 않아도 된다.
AfterReturning Advice
<aop:after-returning>을 이용하여 AfterReturning Advice를 정의한다.
다음은
applicationContext-aop-xml.xml
의
AfterReturning Advice 정의 부분이다. 앞서 정의한 getMethods라는 pointcut을 참조하고 있으며, 해당 pointcut 후에
printStringAspect라는 Bean의 afterReturningExecuteGetMethod() 메소드를 호출해야 함을 명시하고 있다.
또한 해당 Pointcut 실행 결과를 retVal이라는 변수에 담도록 하고 있다.
<aop:after-returning method="afterReturningExecuteGetMethod" returning="retVal"
pointcut-ref="getMethods" />
다음은 AfterReturning Advice를 구현하고 있는
PrintStringUsingXML
클래스의 일부이다.
AfterReturning Advice 역할을 수행하는 afterReturningExecuteGetMethod()는 앞서 정의한 Pointcut 후에 ,
"AfterReturning Advice of PrintStringUsingXML"라는 문자열과 해당 Pointcut을 가진
클래스명, 메소드명을 출력하는 역할을 수행한다.
public class PrintStringUsingXML {
// ...
public void afterReturningExecuteGetMethod(JoinPoint thisJoinPoint, Object retVal) {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out.println("AfterReturning Advice of PrintStringUsingXML");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
// ...
}
afterReturningExecuteGetMethod()는 2개의 입력 인자(JoinPoint, Object)를 가지고 있는데
첫번째 인자는 Target 클래스명, 메소드명 등과 같은 Target 정보를 포함하고 있으며, 두번째 인자는 해당 Pointcut의
실행 결과이다. AfterReturning Advice에서 특정 Pointcut 실행 결과를 참조해야 한다면, XML에 해당 Advice 정의시 returning의
값을 정의하고 해당하는 메소드의 입력 인자명을 동일하게 정의해주도록 한다. 각 입력 인자는 AfterReturning Advice 정의시
필요에 따라 선택 정의할 수 있다.
AfterThrowing Advice
<aop:throwing>을 이용하여 AfterThrowing Advice를 정의한다.
다음은
applicationContext-aop-xml.xml
의
AfterThrowing Advice 정의 부분이다. 앞서 정의한 getMethods라는 pointcut을 참조하고 있으며, 해당 pointcut 후에
printStringAspect라는 Bean의 afterThrowingExecuteGetMethod() 메소드를 호출해야 함을 명시하고 있다.
또한 해당 Pointcut 실행시 발생한 Exception을 exception이라는 변수로 해당 Advice의 입력 인자명과 동일해야 한다.
<aop:after-throwing method="afterThrowingExecuteGetMethod" throwing="exception"
pointcut-ref="getMethods" />
다음은 AfterThrowing Advice를 구현하고 있는
PrintStringUsingXML
클래스의 일부이다.
AfterThrowing Advice 역할을 수행하는 afterThrowingExecuteGetMethod()는 앞서 정의한 Pointcut에서 Exception이 발생한 후에 ,
"AfterThrowing Advice of PrintStringUsingXML"라는 문자열과 해당 Pointcut을 가진
클래스명, 메소드명을 출력하는 역할을 수행한다.
public class PrintStringUsingXML {
// ...
public void afterThrowingExecuteGetMethod(JoinPoint thisJoinPoint, Exception exception) {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out.println("AfterThrowing Advice of PrintStringUsingXML");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
// ...
}
afterThrowingExecuteGetMethod()는 2개의 입력 인자(JoinPoint, Exception)를 가지고 있는데
첫번째 인자는 Target 클래스명, 메소드명 등과 같은 Target 정보를 포함하고 있으며, 두번째 인자는
Pointcut 실행시 발생한 Exception 객체이다. AfterThrowing Advice에서 특정 Pointcut 실행시 발생한 Exception을
참조해야 한다면, XML에 해당 Advice 정의시 throwing의 값을 정의하고 해당하는 메소드의 입력 인자명을 동일하게 정의해주도록 한다.
각 입력 인자는 AfterThrowing Advice 정의시 필요에 따라 선택 정의할 수 있다.
After(finally) Advice
<aop:after>를 이용하여 After(finally) Advice를 정의한다.
다음은
applicationContext-aop-xml.xml
의
After(finally) Advice 정의 부분이다. 앞서 정의한 getMethods라는 pointcut을 참조하고 있으며, 해당 pointcut 후에
printStringAspect라는 Bean의 afterExecuteGetMethod() 메소드를 호출해야 함을 명시하고 있다.
<aop:after method="afterExecuteGetMethod" pointcut-ref="getMethods" />
다음은 After(finally) Advice를 구현하고 있는
PrintStringUsingXML
클래스의 일부이다.
After(finally) Advice 역할을 수행하는 afterExecuteGetMethod()는 앞서 정의한 getMethods()라는 Pointcut 후에
"After(finally) Advice of PrintStringUsingXML"라는 문자열과 해당 Pointcut을 가진 클래스명, 메소드명을
출력하는 역할을 수행한다.
public class PrintStringUsingXML {
// ...
public void afterExecuteGetMethod(JoinPoint thisJoinPoint) {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out
.println("After(finally) Advice of PrintStringUsingXML");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
}
// ...
}
afterExecuteGetMethod()는 1개의 입력 인자(JoinPoint)를 가지고 있는데
Target 클래스명, 메소드명 등과 같은 Target 정보를 포함하고 있다. Target 정보가 불필요한 Advice인 경우에는
JoinPoint라는 입력 인자를 선언하지 않아도 된다.
Around Advice
<aop:around>를 이용하여 Around Advice를 정의한다.
다음은
applicationContext-aop-xml.xml
의
Around Advice 정의 부분이다. updateMethods라는 pointcut을 참조하고 있으며, 해당 pointcut 후에
printStringAspect라는 Bean의 aroundExecuteGetMethod() 메소드를 호출해야 함을 명시하고 있다.
<aop:around method="aroundExecuteUpdateMethod" pointcut-ref="updateMethods" />
다음은 Around Advice를 구현하고 있는
PrintStringUsingXML
클래스의 일부이다.
Around Advice 역할을 수행하는 aroundExecuteUpdateMethod()는 updateMethods()라는
Pointcut 후에 "Around Advice of PrintStringUsingXML"라는 문자열과 해당 Pointcut을 가진
클래스명, 메소드명을 출력하는 역할을 수행한다.
public class PrintStringUsingXML {
// ...
public Object aroundExecuteUpdateMethod(ProceedingJoinPoint thisJoinPoint)
throws Throwable {
Class targetClass = thisJoinPoint.getTarget().getClass();
Signature signature = thisJoinPoint.getSignature();
String opName = signature.getName();
System.out.println("Around Advice of PrintStringUsingXML");
System.out.println("***" + targetClass + "." + opName + "()" + "***");
// before logic
Object retVal = thisJoinPoint.proceed();
// after logic
return retVal;
}
// ...
}
aroundExecuteUpdateMethod()는 1개의 입력 인자(ProceedingJoinPoint)를 가지고 있는데
proceed()라는 메소드 호출을 통해 대상 Pointcut을 실행할 수 있으며, Target 클래스명, 메소드명 등과 같은 Target
정보도 포함하고 있다. 즉, Pointcut 전, 후 처리가 가능하며, Pointcut 실행 시점을 결정할 수 있다.
또한 다른 Advice와는 달리 입력값, target, return 값 등에 대해 변경이 가능하다.
Target 정보가 불필요한 Advice인 경우에는 ProceedingJoinPoint라는 입력 인자를 선언하지 않아도 된다.
Aspect 실행
이제 테스트코드
PrintStringAspectUsingXMLTest
를
이용하여 앞서 언급한 Aspect들이 정상적으로 동작하는지 확인해 보도록 하자.
다음은 테스트코드
PrintStringAspectUsingXMLTest
의
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 PrintStringUsingXML
***class integration.anyframe.services.aop.service.UserServiceImpl.getUser()***
AfterReturning Advice of PrintStringUsingML
***class integration.anyframe.services.aop.service.UserServiceImpl.getUser()***
After(finally) Advice of PrintStringUsingML
***class integration.anyframe.services.aop.service.UserServiceImpl.getUser()***
또한 두번째 로직 userService.getUserWithException(); 실행시 테스트를 위해 getUserWithException() 메소드 내에서
EmpException을 throw 하고 있으므로,
Before, AfterThrowing, After Advice가 적용되어 콘솔창에 다음과 같은 실행
결과를 포함하게 된다.
Before Advice of PrintStringUsingXML
***class integration.anyframe.services.aop.service.UserServiceImpl.getUserWithException()***
...중략...
AfterThrowing Advice of PrintStringUsingXML
***class integration.anyframe.services.aop.service.UserServiceImpl.getUserWithException()***
After(finally) Advice of PrintStringUsingXML
***class integration.anyframe.services.aop.service.UserServiceImpl.getUserWithException()***
다음은 테스트코드
PrintStringAspectUsingXMLTest
의
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 PrintStringUsingXML
***class integration.anyframe.services.aop.service.UserServiceImpl.updateUser()***