이번엔 @Aspect Annotation을 이용해서 AOP를 설정해보도록 하자....
이것 역시 XML 스키마를 이용하여 설정한다..
<beans ...
xmlns:aop=http://www.springframework.org/schema/aop ... >
일단 xmlns에 aop를 추가해주고...
<aop:aspectj-autoproxy/>
위 태그를 사용하면 @Aspect Annotation이 사용된 클래스의 Advice 및 Pointcut을 알맞은 빈 객체에 적용한다...
실제 @Aspect Annotation을 사용한 클래스를 살펴보자..
package test.aspect.annotation
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class TestAspect
{
@Before("execution(public * test.aspect.*.*(..))")
public void Before(JoinPoint joinPoint)
{
System.out.println("Before");
}
@AfterReturning(pointcut = "execution(public * test.aspect.*.*(..))" returning = "ret")
public void AfterReturning(JoinPoint joinPoint, Object ret)
{
System.out.println("AfterReturning");
}
@AfterThrowing(pointcut = "execution(public * test.aspect.*.*(..))" throwing = "ex")
public void AfterThrowing(JoinPoint joinPoint, Throwable ex)
{
System.out.println("AfterThrowing");
}
@After("execution(public * test.aspect.*.*(..))")
public void After(JoinPoint joinPoint)
{
System.out.println("After");
}
@Around("execution(public * test.aspect.*.*(..))")
public void before(ProceedingJoinPoint joinPoint joinPoint)
{
System.out.println("Around");
Object ret = joinPoint.proceed();
}
}
앞서 살펴보았던 POJO를 이용한 설정과 매우 유사하다..
단지 소스 코드에 직접 입력하느냐 설정 파일에 설정하느냐의 차이만 있을 뿐이다...
위의 TestAspect를 생성해주면 test.aspect 패키지의 모든 클래스의 모든 메소드에 AOP가 적용이 된다..
<bean class="test.aspect.annotation.TestAspect"/>
<bean id="test" class="test.aspect.TestService"/>
위의 예제는 test.aspect.TestService의 모든 메소드에 TestAspect가 적용되는 설정이다...
그렇다면 도대체 AspectJ의 Pointcut 표현식이 어떤 뜻인지 궁금해진다..
Spring에서 제공하는 AspectJ의 Pointcut 명시자는 execution과 within 그리고 bean 명시자이다..
먼저 Advice를 적용할 메소드를 명시할 때 사용하는 execution부터 살펴보도록 하자..
execution([접근명시자패턴] 리턴타입패턴 이름패턴(파라미터패턴))
접근명시자패턴은 public, protected, private 등을 사용할 수가 있고 생략 가능하다..
리턴타입패턴은 가능한 모든 리턴 타입을 사용할 수 있고..
이름패턴에는 패키지를 포함한 클래스 이름과 메소드 이름을 사용하고...
파라미터패턴에는 매칭될 파라미터에 대해 명시해준다...
또한 모든 패턴에는 * 를 이용하여 모든 값을 명시할 수 있고 ..을 이용하여 0개 이상이라는 의미를 표현할 수 있다..
예제를 살펴보자....
execution(public void set*(..)) : 리턴 타입이 void이고 set으로 시작하는 파라미터 0개 이상인 public 메소드
execution(* test.aspect.*.*()) : test.aspect 패키지의 파라미터가 없는 모든 메소드
execution(* test.aspect..*.*(..)) : test.aspect 패키지 및 하위 패키지에 파라미터가 0개 이상인 메소드
execution(void test.aspect.TestService.test()) : 리턴 타입이 void인 TestService의 test 메소드 호출
execution(* test*(*)) : test로 시작하는 파라미터 1개를 갖는 메소드
execution(* test*(*, *)) : test로 시작하는 파라미터 2개를 갖는 메소드
execution(* test*(Integer, ..)) : test로 시작하고 첫번째 파라미터는 Integer이고 1개 이상의 파라미터를 갖는 메소드
자세한 내용은 첨부로 올리는 AspectJ Syntax를 참고하도록 하자..
within 명시자는 특정 메소드가 아닌 특정 타입에 속하는 메소드를 명시할 때 사용한다...
within(test.aspect.TestService) : TestService의 모든 메소드
within(test.aspect.*) : test.aspect 패키지의 모든 메소드
within(test.aspect..*) : test.aspect 패키지 및 하위 패키지의 모든 메소드
bean 명시자는 빈의 이름을 이용하여 Pointcut을 정의한다.
bean(test) : 이름이 test인 빈의 메소드
bean(*test) : 이름이 test로 끝나는 빈의 메소드
이상 Spring에서 제공하는 AspectJ 표현식을 알아보았다..
위의 각각의 표현식은 다음과 같이 &&와 ||를 이용하여 연결할 수 있다..
@After("execution(public * get*()) ||execution(public * set*(..))")
이상으로 Spring의 AOP에 대하여 알아보았다..
아무리 봐도 제일 만만한게 Spring API를 사용하는게 아닌가 싶다..