2009/07/08 21:14

PostSharp AOP Framework 정리

 

1) PostSharp Aspect Lifetime

·         PostSharp 핵심은 바로 Post-Compiler입니다. Post-Compiler 컴파일이 이후의 어셈블리를 수정하는 것을 의미합니다.

 

http://www.codeproject.com/KB/cs/ps-custom-attributes-2.aspx


·         PostSharp에서 Aspect 컴파일 타임에 생성(PostSharp Laos 의해서) 됩니다. 또한 런타임에 수정된 어셈블리를 로드하여 사용하는 복잡한(?!) Aspect Lifetime 가지고 있습니다. 이러한 과정을 자세히 알아보겠습니다.

 

·          컴파일 타임 (숫자는 실질적인 시간의 순서를 나타냅니다.)

1. Aspect 인스턴스화 : 해당 Aspect 적용될 모든 클래스(Target) Aspect 적용할 있는 코드를 만듭니다.

2. 컴파일 타임 초기화 : Aspect 인스턴스 안에 타입, 필드, 메소드를 있도록 초기화 해줍니다. 여기서 초기화 하는 타입, 필드, 메소드는 런타임에서도 사용할 있습니다.

3. 직렬화 : 새롭게 만들어진 어셈블리 리소스에 저장시키기 위해 직렬화해줍니다.

 

·          런타임

4. 1. 역직렬화 : Aspect 처음으로 사용하는 시점에 어셈블리 리소스에 저장된 정보들을 역직렬화합니다.

4. 2. 런타임 초기화 : 타입, 필드, 메소드의 속성을 초기화합니다. 여기서는 역직렬화된 필드를 사용하여 역직렬화된 타입, 필드, 메소드를 저장합니다. (다시 말해서 런타임에서는 직렬화된 정보를 역직렬화 하기 때문에 초기화 메서드에서 역직렬화된 필드를 사용하여야 컴파일 타임에 직렬화되었던 정보를 사용할 있는 것입니다.)

5. 인터셉트 메소드 호출 : Aspect 종류에 따라서 Aspect 실행되어야 시점을 인터셉트해서 호출합니다.

 

2) Aspect Kind

종류

내용

인터셉트 메소드

비고

On Method Boundary

메소드의 안에서 일어날 수 있는 시작점과 종료점 등과 같은 인터셉트 메소드를 제공해줌으로써 해당 시점을 인터셉트해 줄 수 있습니다. Trace나 트랜잭션를 사용할 경우에 유용합니다.

OnEntry, OnSuccess, OnException, OnExit

 

On Exception

예외가 발생하는 경우에 처리해야 할 내용을 Aspect로 만들 경우에 사용합니다.

OnException

 

On Field Access

모든 필드에 대한 인터셉트를 제공해줍니다.

OnGetValue, SetValue

 

On Method Invocation

On Method Boundary와 비슷합니다. 차이점은 다른 어셈블리에 있는 메소드도 인터셉트 할 수 있습니다.

OnInvocation

 

Composition

많은 Sub-Aspect를 복합적으로 사용할 경우에 유용합니다.

 

 

Implement Method

abstract or extern 메소드를 인터셉트 해줍니다.

OnExecution

 

Custom Attribute Injector

타켓에 속성을 추가할 경우에 사용합니다.

 

 

 

3) PostSharp + Enterprise Library(이하 EL)

·         EL에서도 AOP의 기능을 하는 Policy Injection Application Block(이하 PIAB)이 있습니다. PIAB는 런타임 위빙방식으로 설정파일에 의해서 Policy(정책)에 의해서 다른 Application Block를 사용합니다. PIAB MarshalByRefObject를 상속받는 개체를 요구하거나 인터페이스를 상속받는 개체를 요구합니다. (실제로 PIAB 예제를 사용해봤을 경우에는 MarshalByRefObject를 상속받지 않고서도 실행이 잘되었는데 좀 더 확인을 해봐야 할 것 같습니다.)
또한 정책에 의해서 다른 AB를 주입해주면서 Proxy, Factory 개체들을 생성하는데 바로 PostSharp4EntLib에서는 이러한 Proxy Factory를 제거하는 것을 목적으로 CodePlex에서 EL의 확장팩 정도의 개념으로 오픈소스로 공개되어 있습니다.

·         PostSharp4EntLib에서는 PostSharp의 컴파일 타임에 정책에 해당하는 객체들을 만들어놓고 런타임에 Proxy Factory 없이 사용하는 방식이라고 할 수 있습니다. 참고자료 2번에 링크로 가보시면 사용하는 방법이 3가지 나옵니다. 두 가지 아쉬운 점은 3가지 방법중에 마지막 방법이 제대로 동작하지 않는 것과 모든 방식이 정책 부분은 컴파일 타임에 만들어지지만 실질적으로 사용되는 AB의 설정은 App.config에서 불러옵니다. 예를 들어서 정책으로 Logging AB를 사용하면 정책은 컴파일 타임에 만들어지지만 Logging AB에 대한 정보는 App.config에 있어야 한다는 것입니다. 그래서 이 부분을 수정하여 현재는 Logging AB에 대해서만 App.config가 아닌 컴파일 타임에 정책이 만들어지면서 .config의 정보를 어셈블리 리소스에 저장하고 런타임에 불러와 사용하는 방식으로 변경해서 사용하고 있습니다.

4) 참고자료

1. PostSharp Doc : http://doc.postsharp.org/

2. PostSharp4EntLib : http://www.codeplex.com/entlibcontrib/Wiki/View.aspx?title=PostSharp4EntLib&referringTitle=Home

3. PIAB : http://msdn.microsoft.com/en-us/library/cc511729.aspx

 

저작자 표시 비영리 변경 금지
Trackback 0 Comment 0
2009/06/17 14:26

Spring VS Unity Container


Composite Application Guidance
를 사용해 보면서 Unity 컨테이너를 처음으로 사용해봤는데요. 이전에 Unity Spring과 달리 코드상에서 컨테이너에 클래스 혹은 객체를 등록해주고 가져오는 정도로만 알고 있었습니다하지만 CAG예제를 만들어 보면서 사용한 Unity의 자동 IoC기능이 Spring보다 훨씬 심플하고 쉽게 접근할 수 있다는 생각이 들었습니다. 기본적인 컨셉이 달라 직접적인 비교는 어떻게 보면 의미가 없어 보이기도 하지만 간단한 예제로 확인해보겠습니다.

먼저 클래스 두개를 아래와 같이 만들었습니다.

public class Children

{

    public Children()

    {

        string msg = string.Format("{0} ({1}) Create", this.GetType().Name, this.GetHashCode());

        System.Diagnostics.Debug.WriteLine(msg);

    }

 

    ~Children()

    {

        string msg = string.Format("{0} ({1}) Finalized", this.GetType().Name, this.GetHashCode());

        System.Diagnostics.Debug.WriteLine(msg);

    }

}

public class Parent

{

    public Parent(Children children) : this()  { }

 

    public Parent()

    {

        string msg = string.Format("{0} ({1}) Create", this.GetType().Name, this.GetHashCode());

        System.Diagnostics.Debug.WriteLine(msg);

    }

 

    ~Parent()

    {

        string msg = string.Format("{0} ({1}) Finalized", this.GetType().Name, this.GetHashCode());

        System.Diagnostics.Debug.WriteLine(msg);

    }

}

=> 객체가 생성되고 소멸되는 것을 출력창에서 확인하게 위한 코드와 Parent 생성자에 파라미터로 Children객체를 받는 간단한 클래스입니다그리고 Spring Unity Dll를 참조합니다.

먼저 Spring App.config(설정파일) Objects를 만들어줘야 합니다. 아래와 같습니다.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <sectionGroup name="spring">

      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>

      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>

    </sectionGroup>

  </configSections>

 

  <spring>

    <context>

      <resource uri="config://spring/objects" />

    </context>

 

    <objects xmlns="http://www.springframework.net">

     

      <object name="children" type="UnityVSSpring.Children, UnityVSSpring" singleton="false" />

     

      <object name="parent" type="UnityVSSpring.Parent, UnityVSSpring" singleton="false">

        <constructor-arg name="children" ref="children" />

      </object>

     

    </objects>

  </spring>

</configuration>

=> parent로 선언된 object를 보면 생성자의 이름과 참조하는 object명이 적혀있습니다. Unity의 경우에는 이러한 환경 설정이 아닌 일반적으로 클래스의 타입을 등록해기만 하면 자동으로 생성자에 IoC의 혜택을 받습니다. 아래는 각각의 컨테이너에서 객체를 가져오는 부분입니다.

 

private void button1_Click(object sender, EventArgs e)

{

    IApplicationContext ctx = ContextRegistry.GetContext();

 

    Parent parent = (Parent)ctx.GetObject("parent");

}

 

private void button2_Click(object sender, EventArgs e)

{

    IUnityContainer unityContainer = new UnityContainer();

 

    unityContainer.RegisterType<Parent>();

 

    Parent parent = unityContainer.Resolve<Parent>();

}

=> 비교해서 보아야 할 부분이 2가지 정도로 보입니다첫 번째로 클래스의 타입을 등록해주는 부분으로 Unity에서는 Parent의 타입만을 등록해주고 있습니다. 하지만 실행을 해보면 생성자로 Children의 타입의 객체를 받는 것을 확인하실 수 있습니다따로 컨테이너에 등록되지 않은 클래스를 생성자나 프로퍼티로 받을 수 있다는 말입니다.
두 번째로는 Unity에서는 타입의 캐스트가 발생하지 않습니다. Spring의 경우에는 object타입으로 가져와서 캐스트를 항상 해주어야 하는 것을 볼 수 있습니다
.

<< 수정 >>

여기서 Unity 컨테이너에 클래스의 타입을 등록해주는 코드가 없더라도 정상적으로 동작합니다. 제 상식에서는 컨테이너에 클래스의 타입이나 객체를 등록해주고 받아올 때는 등록된 클래스와 객체를 가져올 수 있다고 생각했는데 등록을 해주지 않더라도 Resolve<T>에 클래스 타입을 적어주기 때문에 그 타입의 객체를 자동으로 가져옵니다. 생성자에 전달되는 파라미터 객체도 이런 의미로 이해할 수 있습니다. 그렇다면 등록해주는 코드는 왜 필요한걸까요? Unity는 개념이 색다른거 같습니다. ^^; 첫번째로 객체를 Singleton으로 사용하고 싶을 경우입니다. Singleton으로 사용할 경우에는 unityContainer.RegisterType<T>(new ContainerControlledLifetimeManager()); 처럼 사용하면 됩니다. 두번째 이유는 IoC의 기능을 인터페이스를 통해서 받을 때 입니다. 명확하게 Resolve<T>()를 통해서는 인터페이스에 해당 객체가 매핑이 되지만 생성자나 프로퍼티에 인터페이스를 사용한 경우에는 RegisterType<Interface, T>()를 통해서 등록을 해주어야만 주입이 되는 것을 확인할 수 있었습니다.
이상 틀린 내용이 있으면 주저없이 알려주세요~

 
Spring
에서도 Unity처럼 코드상에서 클래스나 객체를 등록해줄 수 있을 것 같은데 찾지는 못했습니다생성자 주입만 자동으로 해주고 Unity에서도 프로퍼티로 주입을 받으려면 어트리뷰트로 [Dependency]를 선언해주어야 합니다.

저작자 표시 비영리 변경 금지
Trackback 0 Comment 0
2009/06/16 17:58

낙관적 동시성


동시성은 둘 이상의 사용자가 동일한 데이터베이스 행을 동시에 업데이트하려는 상황입니다. 오라클에서는 Select해오는 로우에 For Update 구문으로 Lock를 걸어서 처리하기도 합니다. 이와는 반대로 낙관적 동시성은 동시에 업데이트하려는 충돌상황이 발생하였을 경우에 Lock없이 충돌을 해결합니다.
그러면 ADO.NET Entity Framework의 낙관적 동시성에 대해서 알아보겠습니다.

컬럼이 두 개인(C1, C2)인 TxTest라는 테이블을 만들고 C2 컬럼에 Concurrency Mode를 아래의 그림과 같이 Fixed로 변경해줍니다.


아래와 같이 개체서비스를 이용해서 c1컬럼의 값이 "1"인 값을 조회해서 c2의 값을 "New"로 수정해주도록 작성합니다.
using (TestDBEntities context = new TestDBEntities())

{

    var query = from t1 in context.TxTest

                where t1.c1 == "1"

                select t1;

 

    foreach (var item in query)

    {

        item.c2 = "New";

    }

 

    try

    {

        context.SaveChanges();

    }

    catch (OptimisticConcurrencyException opt)

    {

        foreach (ObjectStateEntry item in opt.StateEntries)

        {

            context.Refresh(RefreshMode.ClientWins, item.Entity);

        }

 

        context.SaveChanges();

    }

}


=> 실제로 DB에 저장시키는 SaveChanges()메소드를 try catch 구문으로 묶어주고  OptimisticConcurrencyException 으로 예외를 잡아 충돌이 발생하였을 경우에 실제 DB의 내용을 어떻게 할 것인지 정합니다. Enttity Framework에서는 두 가지 모드가 존재합니다.
1. ClientWins : 마지막 요청을 DB와 개체서비스에 적용시킵니다.
2. StoreWins : 현재 DB의 내용으로 개체서비스와 DB의 내용을 적용시킵니다.

예외를 발생시키기 위해서 먼저 위의 구문을 한번 실행시켜서 c2컬럼에 "New"값으로 채우고 한 번 더 실행시켜서SaveChages() 메소드 부분에서 중단점을 걸어두고 MSSQL 쿼리창을 열어 해당 로우의 값을 "Old"로 업데이트 한 이후에 중단점을 풀어주면 예외가 발생하고 충돌 해결 모드에 따라서 해당 로우의 값이 결정이 됩니다. 1번의 경우에는 c2컬럼에는 "New"의 값이 2번의 경우에는 c2컬럼에는 "Old"의 값이 들어가게 됩니다.

Entity Framework에서 낙관성 모드의 원리는 이렇습니다. 제일 위의 그림에서 c2컬럼에 동시성 모드를 Fixed로 해줌으로써 해당 컬럼을 Update할 경우에 Where절에 Fixed된 컬럼의 값이 들어가게 되고 충돌이 발생하여 다른 Update가 먼저 발생한 경우에 Where절에 Fixed된 컬럼의 값이 들어가기 때문에 Update할 로우가 없게됩니다. 이럴 경우에 OptimisticConcurrencyException를 발생하게 되는 것입니다.
저작자 표시 비영리 변경 금지
Trackback 0 Comment 0