JAVA/SPRING 2009. 5. 6. 11:13

Advice 유형별 인터페이스
 어드바이스 유형 인터페이스 
 Before  org.springframework.aop.MethodBeforAdvice
 After-returning  org.springframework.aop.AfterReturningAdvice
 After-throwing  org.springframework.aop.ThrowAdvice
 Around  org.aopalliance.intercept.MethodInterceptor
 Introduction  org.springframework.aop.IntroductionInterceptor 

Before Advice
target의 특정 메소드를 실행하기 전에 어떠한 일을 추가하고 싶을 경우에 사용합니다.

After-retuning Advice
target객체의 특정 메소드의 실행을 무사히 완료한 뒤에 추가할 작업을 정의 할 때 사용합니다. (예외가 발생하지 않았을 경우)

After-throwing Advice
Advice 가 적용이 되는 메소드에서 예외(Exception)가 발생했을 경우 특정한 일을 추가하고자 할 때 사용합니다.

Around Advice
위에서 살펴본 Before, After-returning, After-throwing 어드바이스의 역할을 모두 다 할 수 있는  강력한 Advice입니다. 또 대상이 되는 메소드의 실행 여부도 결정할 수 있으며, target 메소드의 반환 값을 변경할 수 도 있습니다.

Introduction Advice
위의 어드바이스들과는 다른 매우 특이한 어드바이스로 target객체 자체에 별도의 메소드 또는 속성을 추가합니다.


Classic AOP
MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice 예제 (Spring In Action 참고)

/**

 * 세가지 어드바이스 유형을 구한다. MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice

 *

 */

public class AudienceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {

public AudienceAdvice() {

}


// 메소드 실행전에 호출된다.

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

audience.takeSeats();

audience.turnOffCellPhones();

}


// 성공적으로 메소드가 반환되면 실행된다

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

audience.applaud();

}


// 예외가 발생되면 실행된다.

public void afterThrowing(Throwable throwable) {

audience.demandRefund();

}


private Audience audience;


public void setAudience(Audience audience) {

this.audience = audience;

}

}



  <bean id="audience" class="chapter4.classicSpringAop.Audience" />


  <bean id="audienceAdvice"

class="chapter4.classicSpringAop.AudienceAdvice">

    <property name="audience" ref="audience" />

  </bean>

  

  <bean id="audienceAdvisor"

        class="org.springframework.aop.support.DefaultPointcutAdvisor">

    <property name="advice" ref="audienceAdvice" />

    <property name="pointcut" ref="performancePointcut" />

  </bean>


  <bean id="performancePointcut"

class="org.springframework.aop.aspectj.AspectJExpressionPointcut">

    <property name="expression" value="execution(* perform(..))" />

  </bean>


먼저 맨 밑의 포인트컷 정의 부분을 보면 AspectJExpressionPointcut을 사용하고 있는데 이는 AspectJ 스타일의 표현식을 이용한 것입니다. 

여기서 AspectJ의 표현식을 알아보면

JdkRegexpMethodPointcut(정규표현식)을 이용한다면

  <bean id="performancePointcut"

class="org.springframework.aop.aspectj.JdkRegexpMethodPointcut">

    <property name="pattern" value="*.perform" />

  </bean>

이렇게 쓸 수 있습니다. 

정규표현식을 알아본다면
 기호 의미 
 . 어떤 문자든 딱 한 글자
 *  * 앞에 있는 문자가 여러개 있을 수 있음
 +  + 앞에 있는 문자가 최소한 한 개에서 여러개 있을 수 있음 
 \  \ 뒤에 오는 문자가 있어야한다


DefaultPointcutAdvisor는 단순히 어드바이스(audienceAdvice 참조)와 포인트컷(performancePointcut)을 결합시키는 어드바이저 클래스입니다.

포인트컷과 어드바이스를 동시에 정의할수있는 어드바이저 클래스도 있는데 RegxpMethodPointcutAdvisior입니다.
RegxpMethodPointcutAdvisior를 이용한다면

   <bean id="audienceAdvisor"

        class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

     <property name="advice" ref="audienceAdvice" />

     <property name="pattern" value=".*perform" />

   </bean>

이렇게 쓰고 기존의 포인트컷 부분은 없애 주시면 됩니다.


ProxyFactoryBean 활용

    <bean id="dukeTarget"

      class="chapter2.springidol.PoeticJuggler"

      autowire="constructor">

      <constructor-arg ref="sonnet29"/>  

    </bean>

    

    <bean id="duke" class="org.springframework.aop.framework.ProxyFactoryBean">

      <property name="target" ref="dukeTarget" />

      <property name="interceptorNames" value="audienceAdvisor" />

      <property name="proxyInterfaces" value="chapter2.springidol.Performer" />

    </bean>

    

    <bean id="stevieTarget"

      class="chapter2.springidol.PoeticJuggler"

      autowire="constructor">

      <constructor-arg ref="sonnet29"/>  

    </bean>

    

    <!-- 똑같은 코드의 중복 -->

    <bean id="stevie" class="org.springframework.aop.framework.ProxyFactoryBean">

      <property name="target" ref="stevieTarget" />

      <property name="interceptorNames" value="audienceAdvisor" />

      <property name="proxyInterfaces" value="chapter2.springidol.Performer" />

    </bean>


duke 프록시와 stevie 프록시가 같은 코드가 중복이 된다. 다른 빈들을 만들때도 이런식으로 하면 반복되는 정보가 너무 많아진다. 이럴 때는 추상 빈으로 ProxyFactoryBean을 하나 선언해 두고, 이를 부모삼아 각 프록시 빈에서 재사용 하면 된다.

변경후~~

    <bean id="dukeTarget"

      class="chapter2.springidol.PoeticJuggler"

      autowire="constructor">

      <constructor-arg ref="sonnet29"/>  

    </bean>


    <bean id="stevieTarget"

      class="chapter2.springidol.PoeticJuggler"

      autowire="constructor">

      <constructor-arg ref="sonnet29"/>  

    </bean>

    

    <bean id="audienceProxyBase" 

      class="org.springframework.aop.framework.ProxyFactoryBean"

      abstract="true">

      <property name="interceptorNames" value="audienceAdvisor" />

      <property name="proxyInterface" value="chapter2.springidol.Performer" />

    </bean>

    

    <bean id="stevie" parent="audienceProxyBase">

      <property name="target" ref="stevieTarget" />

    </bean>

    

    <bean id="duke" parent="audienceProxyBase">

      <property name="target" ref="dukeTarget" />

    </bean>



ProxyFactoryBean말고 autoproxing을 사용하자.

위에 작성했던 proxy가 들어가기 전의 코드로 돌아가서~ 프록시땜시 뒤에 붙혀주었던 xxxTarget 은 전부다 원래 빈의 이름으로 바꿔줍시다. 그리고 밑에 요거만 붙혀놓으면... 오토프록시가 설정이되어 할일이 없어집니다... 

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


DefaultAdvisorAutoProxyCreator 클래스는 빈으로 등록만 되면, 자동으로 모든 어드바이저의 포인트컷이 특정 빈의 메서드에 일치하는지 확인하고, 일치하는 빈을 찾으면해당 어드바이스가 적용된 프록시로 대체해 놓습니다. 한마디로, 어드바이저가 일치하는 빈은 모두 자동으로 프록시화 됩니다.

이 방법들 말고 더욱 간단하게 설정하는 방법은 다음으로 미룹니다.
posted by 나는너의힘
: