본문 바로가기
Study_Framework

[Spring] 스프링 API를 이용한 AOP

by 타블로 2008. 10. 21.

● 스프링 API를 이용한 AOP

아래는 스프링API에서 지원하는 인터페이스 Advice입니다.

 인터페이스 
 (org.springframework.aop.support 패키지에 있음)
 간단한 설명
 MethodBeforeAdvice  해당 메소드가 실행 전 실행
 AfterReturningAdvice  해당 멧소드가 실행된 후 실행
 ThrowsAdvice  해당 메소드에서 예외발생시 실행
 MethodInterceptor  해당메소드의 실행전,후와 예외발생시 실행

이제부터는 각각의 인터페이스에서 꼭 구현해야 할 메소드를 간략하게 설명하겠다.

- MethodBeforeAdvice

 void before(Method method, Object[] args, Object target) throws Throwable

method : 대상 객체에서 실행된 메소드를 나타내는 메소드 객체
args : 메소드 인자 목록
target : 대상 객체

- AfterReturningAdvice

 void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable

returnValue : 대상 객체의 메소드가 리턴한 값
method : 대상 객체에서 실행된 메소드를 나타내는 메소드 객체
args : 메소드 인자 목록
target : 대상 객체

- ThrowsAdvice

 void afterThrowing(Method method, Object[] args, Object target, 모든 Exception ex)
 void afterThrowing(모든 Exception ex)

method : 대상 객체에서 실행된 메소드를 나타내는 메소드 객체
args : 메소드 인자 목록
target : 대상 객체
ex : 발생한 예외 타입 또는 상위 타입

- MethodInterceptor

 public interface MethodInterceptor extends Interceptor{
     Object invoke(MethodInvocation invocation) throws Throwable;
 }

invocation : 대상 객체의 모든 정보를 담고 있는 객체(호출된 메소드, 인자 등등) << 약간 설명이 추상적 ㅡㅡ

주로 사용하는 Pointcut과 Advisor 클래스는 다음과 같다.

 org.springframework.aop.support.JdkRegexpMethodPointcut
 org.springframework.aop.aspectj.AspectJExpressionPointcut
 org.springframework.aop.support.RegexpMethodPointcutAdvisor
 org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor 
 org.springframework.aop.support.DefaultPointcutAdvisor

Pointcut 표현 방법에 따라 2가지로 나뉜다(Perl5RegexpMethodPointcut은 제외했다)

- JdkRegexpMethodPointcut : JDK 정규표현식으로 Pointcut 작성
- AspectJExpressionPointcut : AspectJ의 Pointcut 표현식으로 Pointcut작성

아래는 가장 기본적인 Aspect를 적용한 소스이다.

파일이름 : /WEB-INF/config/applicationContext.xml

<!-- 이 빈을 등록하지 않으면 절대로 Aspect가 적용되지 않는다 (경험ㅠ.ㅠ) -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

<bean id="memberDAOAdvice" class="dao.MemberDAOAdvice" />

<bean id="memberDAOPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
    <!-- pattern은 정규표현식이 하나일 때 patterns는 정규표현식이 1개 이상일 때 사용 -->
    <property name="pattern" value=".*Member.*" />

    <property name="patterns">
        <list>
            <value>.*Member.*</value>
        </list>
    </property>
</bean>

<bean id="memberDAOAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="advice" ref="memberDAOAdvice" />
    <property name="pointcut" ref="memberDAOPointcut" />
</bean>

위의 소스는 말 그대로 기본이다.(너무나 쓸데없이 길지 않은가?)
그래서 아래와 같이 간략하게 표현할 수 있다.

파일이름 : /WEB-INF/config/applicationContext.xml

<bean id="memberDAOAdvice" class="dao.memberDAOAdvice" />
 
<bean id="memberDAOAdvisor"
    class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice" ref="memberDAOAdvice"/>
    <property name="pattern" value=".*Member.*" />
</bean>

지금까지는 JDK 정규표현식으로 작성된 Pointcut을 이용하여 Aspect를 적용하는 방법을 설명하였다.
AspectJ 표현식으로 작성된 Pointcut을 이용하여 Aspect를 적용하는 방법은 짧게 소스만 추가하겠다.

파일이름 : /WEB-INF/config/applicationContext.xml

<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
    <property name="expression" value="* *..Member.*(..)" />
</bean>

일단은 Pointcut을 설정하는 부분이다.(소스를 다 적으려다가 필요한 부분만 적었다) 
위의 소스에서 JdkRegexpMethodPointcut 부분을 AspectJExpressionPointcut으로 대체하면 된다.
그리고 AspectJ에서도 간략하게 표현하기 위해 아래와 같이 작성할 수 있다.

파일이름 : /WEB-INF/config/applicationContext.xml

<bean id="memberDAOAdvisor" 
    class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
    <property name="advice" ref="memberDAOAdvice"/>
    <property name="expression" value="* *..Member.*(..)" />
</bean>

이 또한 RegexpMethodPointcutAdvisor 대신에 AspectJPointcutAdvisor로 대체하면 된다.

이렇게 Advisor를 생성만 한다고 Aspect가 적용되는 것이 아니다.
적용하는 방법은 2가지가 있다.

- ProxyFactoryBean을 이용하는 방법
- DefaultAdvisorAutoProxyCreator를 이용하는 방법


둘의 차이점은 각 빈마다 Proxy를 생성하느냐,
각 Advisor(다른 말로 Pointcut)에 맞는 Proxy가 자동생성되느냐의 차이다.

일단 ProxyFactoryBean을 이용하는 방법은 아래와 같다.

파일이름 : /WEB-INF/config/applicationContext.xml

<bean id="memberServiceTarget" class="service.MemberServiceImpl"
    p:memberDAO-ref="memberDAO"
/>
 
<bean id="memberService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="memberServiceTarget" />
    <property name="interceptorNames">
        <list>
            <value>testAdvice</value>
            <value>memberDAOAdvisor</value>
        </list>   
    </property>
</bean>

<bean id="testAdvice" class="aspect.TestAdvice" />

일단 ProxyFactoryBean을 Bean Definition에 등록하고 타켓 클래스 프로퍼티로 해당 서비스 빈을 등록한다.
그리고 interceptorNames 프로퍼티에는 적용할 Advisor 또는 Advice를 등록한다
(ref가 아닌 그저 해당빈의 이름을 인자값으로 넣는다.)
그리고는 memberService를 이용하여 서비스를 하면 해당 Advisor 또는 Advice가 적용이 된다.

다른 방법으로 DefaultAdvisorAutoProxyCreator를 이용하는 방법은 아래와 같다.

파일이름 : /WEB-INF/config/applicationContext.xml

<bean class="or.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

<bean id="memberService" class="service.MemberServiceImpl"
    p:memberDAO-ref="memberDAO"
/>

<bean id="memberDAOAdvisor" 
    class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
    <property name="advice" ref="memberDAOAdvice"/>
    <property name="expression" value="* *..Member.*(..)" />
</bean>

위와 같이 DefaultAdvisorAutoProxyCreator 빈을 등록하고 필요한 Advisor를 등록하면
따로 ProxyFactoryBean을 생성하지 않아도 되고, 위의 ProxyFactoryBean을 이용한 방법과는 달리
해당 서비스 빈이름을 그대로 사용할 수 있다.
(ProxyFactoryBean을 이용할 때는 해당 클래스는 다른 이름(ex : memberServiceTarget)을 사용하고
ProxyFactoryBean의 빈이름을 해당 서비스의 빈이름으로 사용했었다.)


● POJO 클래스를 이용한 AOP

※ 추가 中

● @Aspect 어노테이션을 이용한 AOP

※ 추가 中

참고자료 :
1. 웹개발자를 위한 스프링 2.5 프로그래밍 / 저자 최범균