Gooing deeper

우리는 앞서 처음에 가장 기본적인 advice를 시연해 보았고, 그 다음으로 point cut의 개념을 사용해 advice에 적용해 보았습니다. 하지만 이것이 전부는 아닙니다.  Spring.NET AOP는 제시된 것 보다 더 많고 다양한 advice와 point cut을 사용할 수 있도록 되어 있습니다.

 

advice의 다른 type

앞의 글에서 설명했던 것들은 'around advie' 입니다. 'around advie' 라는 이름이 지어진 이유는 target 메서드의 호출 주변에 advice가 적용되기 때문에 지어진 것입니다. 이전에 정의했던 ConsoleLoggingAroundAdvice의 케이스에서 target은 IMethodInvocation 객체를 이용해 advice를 이용할 수 있게 만들었습니다. 2개의 호출을 만들었었는데 하나의 호출은 target이 호출되기 전에 Console클래스를 만들었던 것이고, 그리고 다른 하나의 호출은 target 메서드 호출 후에 호출이 되는 Console 클래스를 만들었습니다. 이런 구조는 advice를 target으로 둘러 싸게 됩니다.따라서 advice가 완전히 target에 둘러쌓여 있다고 말할 수 있는 것이고 그로 인해 'aroung advice'라는 이름을 가지게 되는 것입니다.

'around advice' 제공자는 target이 하나의 찬스를 얻기 전이나 혹은 반환된 후 다시 말해 양쪽 모두에서 사용될 기회를 가지게 됩니다.

때때로 여러분은 모든 것이 필요하지 않을 때도 있을 겁니다. 다시말해 target 전에 무엇인가를 호출하기를 원한다면 around advice를 사용하면서 target 전에만 호출될 수 있도록 조치를 취하기 보다는 'before advice'를 사용하면 되는 것입니다.

 

Before advice

before advice는 target 객체의 메소드가 호출되기 직전에 호출되기 때문에 MethodInvocation객체가 필요하지 않으며 proceed() 메소드를 호출할 필요가 없습니다.

'before advice'는 단지 target 메소드 호출 전에 호출하기만 하면 됩니다. target 메소드에 굳이 접근하여 원하는 것을 얻을 필요는 없습니다. 다시 말해 하나의 값을 굳이 반환할 필요가 없다는 것입니다. 이것은 매우 좋은 점 입니다. 이것은 target 상에서 proceed() 메소드를 호출할 필요가 없다는 의미로도 해석할 수 있기 때문입니다. 만약에 여러분이 반환값을 변경할 필요가 없다면 심지어 target 메소드 호출의 성공적인 실행 후에 그 어느 것도 필요가 없다면 'before advice'만 사용하면 되는 겁니다.

Spring.NET에서 'before advice'는 Spring.AOP 네임스페이스 내의 IMethodBeforeAdvice 인터페이스에 의해 정의 됩니다. 우리는 간단함을 유지하기 위해 다음의 시나리오를 사용할 것 입니다.

 

'before advice'를 실행하기 위해 먼저 아래와 같이 정의합니다.

public class ConsoleLoggingBeforeAdvice : IMethodBeforeAdvice
{
   public void Before(MethodInfo method, object[] args, object target)
  {
   Console.Out.WriteLine("Intercepted call to this method : " + method.Name);
   Console.Out.WriteLine(" The target is : " + target);
   Console.Out.WriteLine(" The arguments are : ");
   if(args != null)
   {
      foreach (object arg in args)
     {
        Console.Out.WriteLine("\t: " + arg);
     }
   }
 }
}

 

다음으로 ServiceCommand의 Execute() 메소드 호출을 위해 ConsoleLoggingBeforeAdvice의 싱글 인스턴스를 적용해 봅니다. 프로그래밍 설정 방법으로 할 것이고, ConsoleLoggingBeforeAdvice 클래스의 인스턴스로 캡슐화된 새로운 'before advice'를 사용할 것 입니다.

 

ProxyFactory factory = new ProxyFactory(new ServiceCommand());
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();

 

위에 제시된 코드의 실행 결과는 아래와 같이 나올 것입니다.

 

Intercepted call to this method : Execute
The target is : Spring.Examples.AopQuickStart.ServiceCommand
The arguments are :

 

advice 메소드 호출전에 advice가 적용되었고 출력 결과는 보시는 것 처럼 아무것도 없습니다. 왜 그런지 알기 위해 'around advice'와 'before advice'를 대조해봐야 합니다. 우리는 target에서 proceed() 메소드를 호출하는 것을 잊지는 않았습니다. 왜냐하면 'around advice'가 IMethodInvocation에 접근 하는 것이 안되기 때문입니다.

여기에 'before advice'선언을 적용하기 위한 Spring.NET XML 설정이 있습니다.

 

<object id="beforeAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/>
<object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="target">
    <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/>
</property>
<property name="interceptorNames">
    <list>
        <value>beforeAdvice</value>
    </list>
</property>
</object>

 

After advice

'before advice'가 단지 advised target전에 정의되었던 것이라면 'after advice'는 target후에 실행되는 advice가 되는 것입니다.

Spring.NET의 'after advice'는 Spring.AOP 네임스페이스내의 IAfterReturningAdvice' 인터페이스에 의해 정의되어 집니다.

다시 코드를 보도록 하겠습니다. 여러분들은 실습을 위해 이전에 사용했던 예제를 재사용할 수 있을 것입니다.

public class ConsoleLoggingAfterAdvice : IAfterReturningAdvice
{
public void AfterReturning(
object returnValue, MethodInfo method, object[] args, object target)
{
Console.Out.WriteLine("This method call returned successfully : " + method.Name);
Console.Out.WriteLine(" The target was : " + target);
Console.Out.WriteLine(" The arguments were : ");
if(args != null)
{
foreach (object arg in args)
{
Console.Out.WriteLine("\t: " + arg);
}
}
Console.Out.WriteLine(" The return value is : " + returnValue);
}
}

 

ServiceCommand Execute() 메소드의 호출을 위해 ConsoleLoggingAfterAdvice의 싱글 인스턴스를 적용하도록 하겠습니다.

프로그램을 설정함에 있어 우리가 허가해야 할 것은 무엇일까요?

이것은 'before advice'와 같은 방법으로 설정하면 되는 것입니다.

아래의 코드를 참고하세요.

ProxyFactory factory = new ProxyFactory(new ServiceCommand());
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();

 

그리고 이것의 결과는 다음과 같을 것 입니다.

This method call returned successfully : Execute
The target was : Spring.Examples.AopQuickStart.ServiceCommand
The arguments were :
The return value is : null

 

advised 메소드 호출 후에 적용된 advice의 출력결과가 깨끗한 것을 확인할 수 있습니다. 우리는 분명히 target에서 Proceed() 메소드의 호출을 잊지는 않았습니다. 이것의 이유는 'before advice' 처럼 IMethodInvocation 에 의해 접근이 되지 않기 때문입니다.

target의 반환값에 접근하여 얻기 위해선 또 다른 반환값을 반환한다는 것을 우리는 잊어서는 안됩니다. 일반적으로 프로퍼티를 약간 셋팅하거나 메소드 호출에 의해 리턴값의 상태를 변경할 수 있을 겁니다.

 

'after advice'를 위한 최선의 규칙은 'before after'와 같습니다.

바로 아래에 Spring.NET의 설정이 있습니다.

<object id="afterAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/>
<object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="target">
<object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/>
</property>
<property name="interceptorNames">
<list>
<value>afterAdvice</value>
</list>
</property>
</object>

 

Throws advice

우리는 'around advice', 'before advice', 'after advice'를 살펴 봤습니다.

Spring.NET에서 남은 advice 타입이 하나 더 있는데 'throw advice' 입니다. 'throws advice'는 exception을 통해 advised methodrk 호출될 때 실행됩니다. 'throws advice' 는 target 객체에 기본적으로 적용됩니다.

어떤 응용 프로그램이든 advised 메소드는 반드시 실행되게 되어 있습니다. 그러므로 여러분의 응용 프로그램에서 advised 메소드가 실행되지 않으면 'throws advice' 를 실행할 것입니다.

'throws advice' 는 여러분의 응용 프로그램내의 다양한 변수를 통한 공통 실행 핸들링 정책에 적용할 수 있을 것입니다.

Spring.NET 내의 'throws advice' 타입은 Spring.AOP 네임스페이스 내의 IThrowsAdvice에 의해 정의 됩니다. IThrowsAdvice 인터페이스를 간단하게 살펴보면 아래와 같습니다.

public interface IThrowsAdvice: IAdvice

{

}

 

아래의 코드는 'throws advice' 의 간단한 Spring.NET 스타일 입니다.

public class ConsoleLoggingThrowsAdvice : IThrowsAdvice
{
public void AfterThrowing(Exception ex)
{
Console.Out.WriteLine("Advised method threw this exception : " + ex);
}
}

 

throws 실행 역시 이전의 예제 처럼 ServiceCommand 클래스의 Execute() 메소드의 실행을 변경할 수 있고 ConsoleLoggingThorwsAdvice에 의해 advice가 캡슐화되어지는 것을 허락할 수 있을 것입니다.

public class ServiceCommand : ICommand
{
public void Execute()
{
throw new UnauthorizedAccessException();
}
}

 

위의 ServiceCommand 클래스의 Execute() 호출에 'throws advice'를 적용해 보겠습니다.

ProxyFactory factory = new ProxyFactory(new ServiceCommand());
factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();

 

적용된 결과는 아래와 같습니다.

Advised method threw this exception : System.UnauthorizedAccessException:
Attempted to perform an unauthorized operation.

 

출력에서 보듯 ConsoleLoggingThrowsAdvice 호출시 예외를 던졌습니다.

Spring.NET에서 'throws advice'는 ConsoleLoggingThrowsAdvice advice 클래스에 관해 주의해야 할 점이 몇 가지 있습니다.

Spring.NET에서 'throws advice'는 IThrowsAdvice' 인터페이스의 구현인 클래스를 정의할 때 꼭 가지고 있어야 합니다.

 

void AfterThrowing(Exception ex)

 

기본적으로 여러분의 Exception 핸들링 메소드는 AfterThrowing 이라는 이름을 가지게 됩니다.

이 이름은 매우 중요합니다. 왜냐하면 여러분의 Exception 처리 메소드는 절대적으로 AfterThrowing을 호출해야 만 하기 때문입니다. 만일 여러분의 처리 이벤트가 AfterThrowing을 호출하지 않는다면 마찬가지로 'throws advice' 역시 호출되지 않기 때문입니다.

 

예외 처리 방법은 예외타입매개변수를 선언하는 것입니다.

이 매개변수는 루트예외클래스가 될수도 있고 또는 특정 예외를 처리하는 서브 클래스 일수도 있습니다.

여러분의 예외처리 메서드를 만드는 가장 좋은 예는 예외 파라미터를 가지는 것이고, 그것을 통해 특별한 예외 타입 처리를 가능하게 해주기 때문입니다.

ArgumentException을 처리하는 방법은 아래와 같습니다.

 

void AfterThrowing(ArgumentException ex)

 

여러분의 예외처리메소드가 어떤 반환 타입을 가질수 있는가에 대해 살피고 체크하고 또 주의해야 하는게 어찌보면 지극히 정상적인 그리고 당연한 걸수도 있겠습니다. 그러나 Spring.NET 'throws advice'로 부터 어떤 값이 반환되는 것을 여러분 모두가 체크하고 알아내는 것이 어쩌면 시간 낭비가 될수도 있겠습니다.

그런 이유로 Spring.NET 인프라스트럭처는 반환값을 간단하게 무효화 합니다. 그래서 항상 여러분만의 예외처리메소드를 피하기 위해 반환값을 정의해야만 합니다.

 

여기에 'throws advice'선언을 위한 Spring.NET XML 구성이 있습니다.

<object id="throwsAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/>
<object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="target">
<object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/>
</property>
<property name="interceptorNames">
<list>
<value>throwsAdvice</value>
</list>
</property>
</object>

 

'throws advice' 구현 내에서 예외처리방법을 정의할 수 있는 하나의 가장 가까운 방법은 예외를 또 다른 예외로 감싸는 처리를 하는 것입니다. 우리는 하나의 예외처리방법을 본문으로 감싸서 예외를 던질 수 있을 것입니다.

특정한 구현에 대한 예로 SqlException 이나 OracleException과 같은 서비스 개체를 의미있는 비즈니스 예외로 교체하는 것을 구현하기 위해 이것을 사용하게 되는데 'throws advice'의 예제는 아래에서 볼 수 있습니다.

public class DataAccessExceptionScrubbingThrowsAdvice : IThrowsAdvice
{
public void AfterThrowing (SqlException ex)
{
// business objects in higher level service layer need only deal with PersistenceException...
throw new PersistenceException ("Cannot access persistent storage.", ex.StackTrace);
}
}

 

Spring.NET 데이터 액세스 라이브러리는 이미 이러한 종류의 기능을 가지고 있습니다.

위의 예제는 단지 설명을 목적으로 하기 위해 사용된 것입니다.

Spring.NET 에서 'throws advice'의 구현은 간단합니다.

'throws advice'의 특징은 예외처리방법을 정의할 수 있다는 것이고, 원본 객체 메소드에 접근이 허락되며, advised 메소드의 메소드 인자를 원본 예외가 던져질 때 호출할 수 있다는 것입니다.

이러한 부분에 대한 보다 상세한 설명은 Spring.NET 레퍼런스 문서의 13.3.2.3의 'throws advice'를 참고 하시면 됩니다.

 

mixins에 대한 소개

여러분은 새로운 상태나 행위를 임의의 객체에 추가하기를 원할 수 있습니다. 지금까지 앞서 설명한 모든 예제는 단일 advice를 증명하기 위한 것입니다. 오직 단 하나의 advice 인스턴스를 적용할 수 있다면 Spring.NET의 AOP는 결코 완전하다고 볼수 없을 것입니다.

아래의 코드를 통해 이러한 제약을 Spring.NET 이 벗어날 수 있다는 것을 알수 있을 것입니다.

 

ProxyFactory factory = new ProxyFactory(new ServiceCommand());
factory.AddAdvice(new ConsoleLoggingBeforeAdvice());
factory.AddAdvice(new ConsoleLoggingAfterAdvice());
factory.AddAdvice(new ConsoleLoggingThrowsAdvice());
factory.AddAdvice(new ConsoleLoggingAroundAdvice());
ICommand command = (ICommand) factory.GetProxy();
command.Execute();

 

여러가지 advice가 동시에 사용된 것을 확인할 수 있을 것입니다.

이 보다 더 복잡하게 사용될 수도 있겠지만 어쨋든 advice 인스턴스는 이렇듯 다양하게 적용해 사용할 수 있습니다.

아래는 이것에 대한 XML 코드이고, 이 코드는 Spring.AopQuickStart.Step2 프로젝트내의 AopQuickStart솔루션 내에서 찾을 수 있을 것입니다.

 

 





profile

소프트웨어라는 도구를 천문분야에 접목하는 것에 대해 관심을 가지고 있는 1인 입니다.

 

 

한국천문연구원(KASI) 한국형 외계지적생명체 탐색 기술자문 (2010)

Microsoft Visual C# MVP(2009-2010)

세티의 Lonely Star 블로그 운영


facebook: http://www.facebook.com/setipark