posted by 방랑군 2012. 1. 10. 00:00
출처 :   http://dncblog.tistory.com/53 

Spring은 이미 오래전부터 자바진영에서 각광받고 있는 프레임웤으로서, 닷넷버전으로도 공개가 되었습니다. 
그것이 바로, Spring.NET인데요,   
Spring.NET은 닷넷기반의 엔터프라이즈 어플리케이션을 구축하기 위한 어플리케이션 프레임웤  이라고 정의할 수 있습니다. 



Spring.NET의 각 모듈의 구성을 보시면, 하단 우측의 AOP 기반으로, ASP.NET,  Web Serivce, ORMapping, DataAccess, NUnit, Remoting, COM+ 등의 다양한 코어 모듈 기반의 프레임웤입니다. 

AOP(Aspected Oriented Programming) 란  관점 지향 프로그래밍이라고 해석되는데요, 
이는, 프로젝트를 진행할때,  개발자가 고민해야하는 비지니스 로직을 핵심관심사라 보고, 그외의 인증/권한/예외처리/로깅/트랜젝션관리 등에 대한 것을 횡단관심사로 보아, 관심의 분리를 일컷습니다. 
이는, 개발자는 핵심관심사에 치중하며, 횡단관심사는 프레임웤의 각 컨테이너가 제공을 해주게 됩니다. 
그리고 각 컨테이너는 IOC(Inversion Of Control) 형태로 구성되어있는데요,  
기존에 개발방법론에서는 작성되어있는 프레임웤은 Library로서의 기능이 강했습니다. 
개발자는  비지니스 로직에 집중하면서,  횡단 관심사에 대한 호출이 필요한 경우에는 작성되어있는 프레잌웤 라이브러리를 호출해서 사용하였었지요. 
그러나, IOC 형태로 구성된 컨테이너 들은  프레잌웤 코드가 전체 어플리케이션의 처리흐름을 제어하며, 특정한 이벤트가 발생할 시마다 다형성을 통해, 어플리케이션이 확장한 메소드를 호출함으로서, 결과적으로는 제어가 프레임웤에서 어플리케이션으로 거꾸로 흐르게 되는 방법을 말합니다. 
그리고 객체가 참조하고 있는 다른 객체를 스스로가 new로 create하지 않고, 인터페이스를 통해서 Assing받아 사용하는 방식을 따르고 있습니다.

[예제1]  
App.config 
    <objects xmlns="http://www.springframework.net" >
       <object id="MyHello" type="DI.Hello, DI"></object>
    </objects>

Interface 
    interface IHello
    {
        string sayHello(string str); 
    }

Class 
    class Hello : IHello
    {
        public string sayHello(string str)
        {
            return str; 
        }
    }

MainApp
        static void Main(string[] args)
        {
            IApplicationContext ctx = ContextRegistry.GetContext();  //app.config에 설정된 object를 가져옵니다. 
            IHello hello = (IHello) ctx.GetObject("MyHello"); //  MyHello object를 노출된 interface 를 통한 Assign 
            string str = "안녕";
            string result = hello.sayHello(str);  
            Console.WriteLine(result);
        }

MainApp에서 Hello 클래스의 객체를 직접 create하지 않고, IHello 인터페이스를 도입함으로서, MainApp와 Hello와의 종속성이 제거되었습니다.  여기서 Hello가 컴퍼넌트 역할을 하게됩니다.
configuration에 사용하고자 하는 컴퍼넌트를 명시하고,  MainApp에서는  해당 컴퍼넌트를 사용할수 있도록 interface를  제공한 것입니다. 
위와 같은 프로그래밍 모델을  IOC, DI 라고 부릅니다. 


Spring.NET에서는 AutoProxy기능이 지원되는데요,  이는 특정 메소드가 호출될때, configuration에 define된 룰에 따라, 메소드호출을 intercept하여, 흐름의 제어를 할수 있게 되는것입니다. 

[예제2]
 
 
Configuration
<!--AutoProxy 설정-->
      <object id="MyServiceCommand" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
        <property name="ObjectNames">
          <list>
            <value>K*</value>
          </list>
        </property>
        <property name="InterceptorNames">
          <list>
            <value>Interceptor</value>
          </list>
        </property>
      </object>
  <!-- Intercept시 호출된 클래스정보-->
  <object id="Interceptor" type="ConsoleApplication2.Interceptor, ConsoleApplication2"/>
  
 <!--Object-->
      <object id="English" type="ConsoleApplication2.HelloWorldSpeaker, ConsoleApplication2">
        <property name="Language" value="English"/>
      </object>
     <object id="Korean" type="ConsoleApplication2.HelloWorldSpeaker, ConsoleApplication2">
        <property name="Language" value="Korean"/>
      </object>
  
interface
    public interface IHelloWorldSpeaker
    {
        void SayHello();
    }

Class 
   public class HelloWorldSpeaker : IHelloWorldSpeaker
    {
       public void SayHello()
        {
            switch (language)
            {
                case Language.English:
                    Console.WriteLine("영어");
                    break;
                case Language.Korean:
                    Console.WriteLine("한국어");
                    break;
            }
        }
    }

Interceptor 
 public class Interceptor : IMethodInterceptor
    {
        public object Invoke(IMethodInvocation invocation)
        {
            Console.WriteLine("실행전");
            object rval = invocation.Proceed();
            Console.WriteLine("실행후");
            return rval;
        }
    }

또한 before/After/Around 등의 Advice를  configuration에 세팅하게되면, 메소드의 실행전후에 특정 흐름을 끼워넣을수도 있게 됩니다. 

Spring.NET에서는 Quart.NET을 이용한  스케줄러 구현을 지원합니다.일반적으로, 기존에 우리 스케줄러를 작성하기 위해서는 쓰레드개념을 생각 안할 수가 없는데요, 
이때, Threadpool을 관리함에 있어서 여러가지 고민해야될것들이 많았습니다 
Pool의 count , 객체 소멸시점, 특정 시간에만 동작하기 위한 Timer .. 외에도 여러가지 존재할것 같습니다. 
그렇다면, Spring.NET에서는 이 모든것들에 대해서 어떻게 구현하게 될까요.. 
이제부터 직접 스케줄러를 구현해 보도록 하겠습니다. 

1. configuration  
2. scheduler
3. trigger 
4. jobDetail 

configration을 작성해야 하는데요, 이는 app.config에 작성할 수도 있고, 따로 xml파일로 뺼수도 있습니다. 
configuration에는 스케줄에 해당하는 object와, 스케줄에 대한 반복적인 주기를 뜻하는 트리거, 그리고 마지막으로 각 트리거가 수행하는 Job에 대해서 명시해야합니다. 

[예제3]
 
Scheduler
<object id="quartzSchedulerFactory"  type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
    <property name="SchedulerName" value="quartz1"/>
    <property name="triggers">
      <list>
        <ref object="CronTrigger" />
        <ref object="SimpleTrigger" />
       </list>
    </property>
  </object>

Trigger  
<object id="SimpleTrigger"  type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
    <!-- see the example of method invoking job above -->
    <property name="JobDetail" ref="SchedulerJob" />
    <!-- 10 seconds -->
    <property name="StartDelay" value="10s" />
    <!-- repeat every 10 seconds -->
    <property name="RepeatInterval" value="10s" />
  </object>
  <object id="CronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz">
    <property name="JobDetail" ref="SchedulerJob" />
    <!-- 5초마다의 interval Fire -->
    <property name="CronExpressionString" value="0/5 * * * * ?" />
  </object>

Job
<object name="SchedulerJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz">
    <property name="JobType" value="Publisher.SchedulerJob, Publisher" />
    <property name="JobDataAsMap">
      <dictionary>
        <entry key="Timeout" value="5" />
       </dictionary>
    </property>
  </object>

모든 object는 Id(Name)와 Type을 지정해주어야합니다. 
그리고, 각 오브젝트마다 가지는 프로퍼티가 조금씩 다른데요, 
스케줄에서는 해당 스케줄에 대한 반복적인 주기를 뜻하는 트리거를 지정하게됩니다. 
저는 simpleTrigger와 cronTrigger라는 두개의 트리거를 지정했는데요,  몇분에 한번씩. 처럼 간단한 작업주기인경우에는 simpleTrigger를, 일중일 중에서 토/일을 제외한 매일 아침 10시부터 12시까지 20분간격으로. 라는 조금은 복잡해보이는 반복주기인경우에는 crontrigger를 사용하면 됩니다. 
그리고 각 트리거에서는 수행할  Job에 대해서 명시를 해야하는데요, 
JobType이라는 프로퍼티를 통해, 수행할 Task의 Type을 지정하게됩니다 Type은 어셈블리명, 네이스페이스명으로 지정하시면 됩니다.
위의 configration는 Publisher.SchedulerJob을 하나는 10초마다, 하나는 5초마다 반복 수행하는 스케줄러임을 알수 있게됩니다. 
config세팅이 끝나면, 이제 Publisher.SchedulerJob을 작성해 보겠습니다. 

[예제4]
 class SchedulerJob : QuartzJobObject
    {
        protected override void ExecuteInternal(JobExecutionContext context)
        {
                 Console.WriteLine("Excute..."); 
         }
    }


SchedulerJob은 QuartzJobObject를  상속받아, 예약되어있는 ExecuteInternal메소드를 오버라이딩 하면 됩니다 
파라메터인 JobExecutionContext타입의 context에는 configration에서 작성한 해당 Job의 정보를 모두 알 수 있습니다. 
위와 같이 QuartzJobObject를 상속하여, Job을 수행하는 방식 외에, 직접 TargetMethod를 지정할 수도 있습니다.

스케줄 트리Listener를 등록해 놓으면, 트리거가 Fire될때, 혹은 misFire되거나 실행이 완료되었을때의 이벤트를 Listen할 수가 있습니다.

[예제5]
   
 public class TriggerListener : ITriggerListener
    {
        
public void TriggerFired(Trigger trigger, JobExecutionContext context)
        {
            Console.WriteLine("TriggerFired");
        }

         public void TriggerMisfired(Trigger trigger)
        {
            Console.WriteLine("TriggerMisfired");
        }

        public void TriggerComplete(Trigger trigger, JobExecutionContext context,
                                    SchedulerInstruction triggerInstructionCode)
        {
            Console.WriteLine("TriggerComplete");
        }
    }

작성된 스케줄러를 실행시켜보겠습니다. 
[실행결과]

5초마다 Crontrigger가, 10초마다 simpleTrigger가 수행되고 있으며, 각 이벤트가 Fire될때마다 Trigger를 Listen하고 있습니다.

지금까지 간략하게나마, Spring.NET을 이용하여 스케줄러를 만들어 보았는데요,, 
잘 동작하네요 :)  

읽어주셔서 감사합니다.   

'PP > SPRING.NET' 카테고리의 다른 글

일반 구성 설정(ASP.NET)  (0) 2012.01.13
Spring .Net 활용 가이드  (0) 2012.01.12
DI  (0) 2012.01.10