이번 글에서는 Spring.NET 으로 Aspect Oriented Programming(AOP)를 하기 위한 내용을 다뤄 보겠습니다. AOP에 대한 경험이 없어도 이 글을 이해하는데 전혀 무리가 없겠지만 그래도 그것에 대한 경험이 조금이라도 있다면 훨씬 도움이 될것 입니다.

이 글에서 제시하는 예제는 매우 간단합니다. 그리고 이 간단한 예제를 통해 가능한 짧은 시간에 AOP에 대한 이해와 실행을 해볼수 있을 겁니다. 하지만 Spring.NET AOP를 100% 완벽하게 이해하기 위해서는 이 짧은 예제만 가지고는 도움이 되지 않을수도 있습니다. 이것에 대한 이해를 보다 확실히 하기 위해선 아무래도 여러분 스스로가 여기서 가이드하는 내용을 토대로 지속적으로 자신의 코드에 적용해 보는 노력을 기울여야 합니다.


이 글에서 사용하는 예제는 Spring.NET 샘플코드 이며, 해당 샘플은 \examples\Spring\Spring.AopQuickStart 부분에 위치합니다.

그리고 예제를 살펴보기 위해선 이 글에서 사용하는 몇 가지 Spring.NET 용어에 대한 이해가 필요합니다. 


용어는 다음과 같습니다.

Aspect - 여러가지 객체를 교차하여 조립하는 것

Join point - 특정한 작업이 실행되는 시점

Advice - 특정한 시점에 실행되는 기능을 말하며, 이것의 타입으로는 before, after, around 가 있습니다.

Pointcut - Advice는 하나의 Joint point 표현에 연결되고 실행됩니다.

Target object - 실질적인 로직을 구현하는 객체이며, 이것은 aspect에 실행될 객체 입니다.

AOP Proxy - aspect contract 명령으로 AOP 프레임워크에 의해 생성되는 객체 입니다.


그럼 첫번째로 advice객체를 적용해 보겠습니다.

아래의 예제는 시스템 콘솔에 advice를 적용하여 호출되게 하는 코드 입니다.


public interface ICommnad

{

object Excute(object context);

}

public class ServiceCommand : ICommand

{

public object Execute(object context)

{

Console.Out.WriteLine("Service implementation : [{0}]", context);

return null;

}

}


아래의 코드는 around advice인데 ServiceCommand 클래스의 object Execute(object context) 메서드가 적용되는 부분을 아래의 코드에서 찾아보세요.

public class ConsoleLoggingAroundAdvice : IMethodInterceptor

{

public object Invoke(IMethodInvocation invocation)

{

Console.Out.WriteLine("Advice executing; calling the advised method...");

object returnValue = invocation.Proceed();

Console.Out.WriteLine("Advice executed; advised method returned " + returnValue);

return returnValue;

}

}


찾으신 분도 있고, 못찾으신 분도 계실 겁니다.

못찾으신 분들을 위하여 위의 코드에 대해 간단하게 설명하겠습니다.

1) Console.Out.WriteLine("Advice executing; calling the advised method...");

   이 부분은 advice가 실행되는 사실을 출력하는 간단한 코드 입니다.


2) object returnValue = invocation.Proceed();

    이 부분은 advised 메서드가 호출되고 returnValue라는 변수에 리턴값이 할당됩니다.


3) Console.Out.WriteLine("Advice executed; advised method returned " + returnValue);

    이 코드는 returnValue 변수가 가진 값을 출력하는 코드 이며


4) return returnValue;

    마지막으로 이 코드는 이 객체를 호출한 곳으로 값을 리턴하는 코드 입니다.



우리는 위에 제시된 코드 다시 말해 총 3개의 아티클을 가지고 있는데요 그것은 인터페이스인 ICommand와 인터페이스의 실행인 ServiceCommand 그리고 ConsoleLogginAroundAdvice 클래스에 캡슐화 되어 있는 advice를 가지고 있습니다.

이 3가지를 ServiceCommand 클래스에 있는 Execute() 메서드의 호출시에 실제로 적용해 보는 것을 해보겠고, 프로그래밍 방식으로 적용해보겠습니다.


ProxyFactory factory = new ProxyFactory(new ServiceCommand());

factory.AddAdvice(new ConsoleLoggingAroundAdvice());

ICommand command = (ICommand) factory.GetProxy();

command.Execute("This is the argument");


실행된 위 코드의 결과값은 아래와 같습니다.

Advice executing; calling the advised method...

Service implementation : [This is the argument]

Advice executed; advised method returned


위에 출력된 결과값에서 2번째 줄을 보면 Service implementation : [This is the argument] 라는 부분이 있습니다.

이것은 어떻게 출력되게 된 것일까요?


일단은 위의 코드가 ProxyFactory클래스를 사용한 것은 확실해 보입니다. 그리고 이 ProxyFactory클래스 생성자는 advise를 위해 ServiceCommand 클래스의 인스턴스를 인자로 가지고 있습니다. 우리는 ProxyFactory 인스턴스의 AddAdvice() 메서드를 사용하여 Advice(ConsoleLoggingAroundAdvice 인스턴스)를 추가했습니다. 그런 후 ProxyFactory 인스턴스의 GetProxy() 메서드를 호출했습니다. Proxy는 Target Object인 ServiceCommand 인스턴스를 가져옵니다. 그리고 advice(ConsoleLoggingAroundAdvice의 싱글 인스턴스)를 호출합니다. 따라서 우리가 Proxy의 Execute(object context) 메서드를 호출할 때 advice는 실행되고 우리는 그 결과를 보게 되는 것입니다.


다음의 이미지는 Spring.NET AOP 프록시를 통해 실행되는 플로우를 그래픽으로 보여줍니다.

CallingCode.jpg


여기서 한가지 주의할 점이 있는데 AOP 프록시가 ProxyFactory인스턴스의 GetProxy() 메서드의 호출에 의해 반환되고 ServiceCommand Target Object는 ICommand 인터페이스로 캐스팅 되었다는 것입니다. 이것은 매우 중요한 사실입니다. 왜냐하면 현재 Spring.NET AOP 구현은 advised 객체 인터페이스 사용함으로써 위임됩니다. 다시말해 이것의 의미는 클래스에 Spring.NET AOP 지원을 위해 적어도 하나의 인터페이스가 구현되어야 함을 말합니다. 이러한 제한이 부담이 될수도 있겠지만 일반적으로 이러한 방식은 어쨋든 좋은 방식 입니다.


선언적 스타일의 프로그래밍을 좋아하는 분들은 다음과 같이 Spring.NET XML 구성하여 코딩 할수 있습니다.

<object id="consoleLoggingAroundAdvicetype="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/>

<object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">

<property name="target">

<object id="myServiceObjectTargettype="Spring.Examples.AopQuickStart.ServiceCommand"/>

</property>

<property name="interceptorNames">

<list>

<value>consoleLoggingAroundAdvice</value>

</list>

</property>

</object>


ICommand command = (ICommand) ctx["myServiceObject"];

command.Execute();


이것은 기본적으로 앞에 작성한 예제와 같습니다.

XML 구성에 대해 설명하자면 첫째로 ConsoleLoggingAroundAdvice객체는 여느 다른 객체들 처럼 구성이 될수 있습니다. 만약에 advice 그 자신에 의존성 주입이 필요하다면 다른 것들과 마찬가지로 정상적으로 주입이 가능합니다.

두번째로 IoC 컨테이너에 의해 해당 객체 정의가 검색될 때 ProxyFactoryObject라는 것을 확인하게 됩니다. ProxyFactoryObject클래스는 IFactoryObject인터페이스의 구현입니다. IFactoryObject구현은 Spring.NET IoC컨테이너에 의해 특별하게 처리 됩니다. ProxyFactoryObject 인스턴스는 참조를 반환하지 않고 ServiceCommand클래스의 advised 인스턴스가 될 것입니다.

세번째 ProxyFactoryObject의 Target Object는 ServiceCommand 클래스의 인스턴스 입니다. 이것은 advised 객체 이고 이 객체의 인스턴스는 내부객체정의로서 정의됩니다. 이것이 의미하는 바는 다른 객체는 원시객체에 대한 참조를 취득하지만 ProxyFactoryObject는 참조를 취하지 않는다는 것입니다.

마지막으로 Target object에 적용된 advice는 ProxyFactory객체의 인터셉터이름 속성을 위한 인터셉터의 이름 리스트내의 객체 이름에 의해 참조됩니다.





profile

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

 

 

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

Microsoft Visual C# MVP(2009-2010)

세티의 Lonely Star 블로그 운영


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