使用Castle DynamicProxy (AOP)


在本文中,我將引導您了解.NET環境中的面向方面編程(AOP)概念,以及如何使用Castle DynamicProxy創建和附加方面在我們開始之前,讓我快速介紹AOP和  IoC如果您已經熟悉這些概念,則可以完全跳過本節。

什么是AOP?

方面 - 面向對象編程  ( AOP)是一種 編程 范式,旨在通過允許的橫切關注分離,以增加模塊性。 一個方面是通常分散在方法,類和對象層次結構中的常見功能。看起來像它的行為具有結構,但是找不到使用傳統的面向對象技術來表達它的方法。

一個很好的例子就是日志記錄,我將在本文中詳細討論。通常,您在代碼庫中寫入信息豐富的日志,但日志記錄是您的類或對象模型真的不應該關心的,因為它不代表域對象。

使用AOP方法,我們可以創建這些交叉關注的方面,並使用多種技術將它們集中在域對象上。IL代碼編織和截取是廣泛使用的方法。在本文中,我將介紹使用Castel Windsor框架動態創建和應用方面的過程。

控制反轉(IoC)/依賴注入(DI)容器

IoC容器是一種在需要時自動創建和注入依賴項的框架。DI容器幫助我們以簡單和更有效的方式管理應用程序中的依賴關系。

大多數主流DI(依賴注入)容器都內置支持攔截。使用這種方法,您可以在運行時調用和更改域對象的行為來攔截該方法。我們將利用此功能將方面附加到我們的域對象。我的DI框架選擇是城堡溫莎,其DynamicProxy是應用方面的流行方式之一。

Code Project中有很多很好的文章和不同的博客,可以為您提供有關此(IoC)主題的更多詳細信息。關於IoC的詳細討論超出了本文的范圍。 

攔截使用Castle DynamicProxy

Castle DynamicProxy是一個用於在運行時生成.NET代理的庫。它允許您動態地更改和擴展業務對象的行為。這使得您的域模型更加可維護,因為交叉關切純粹與核心域模型脫鈎。如果為任何組件指定攔截器,Castle將自動創建代理。您使用攔截器將代理注入行為。

你可能想知道這整個事情在內部如何工作。每當調用者請求業務對象(具體類)時,IoC容器將在DynamicProxy的幫助下解析並將其包裝在包含指定攔截器的代理對象中。容器然后將代理的對象返回給調用者。來電者然后直接與代理人進行交互。代理攔截對業務對象的每個方法調用,並讓請求流過攔截器管道。

 

下圖顯示了請求如何流入代理。您可以看到請求在實際執行方法之前和之后通過所有攔截器。

在您的項目中設置Castle DynamicProxy的步驟

  • 從NuGet下載並安裝“Castle.Windsor”軟件包。
  • 實現IInterceptor接口。這是DynamicProxy將要使用的接口。
  • 實施IRegistration界面並注冊您的組件。注冊攔截器后跟業務組件。指定要與每個業務組件一起使用的攔截器。
  • 創建Windsor容器(IWindsorContainer)的靜態實例,使用組件注冊信息進行初始化。

這幾乎是配置Castle DynamicProxy所需要的!

使用代碼

讓我們從我們的示例應用程序開始。此應用程序包含一個業務對象“火箭”,我們使用控制台應用程序啟動。

接口包含一個稱為“啟動”的單一方法簽名。 

  public interface IRocket
    {
        void Launch(int delaySeconds);
    }

 

允許通過實現唯一需要的方法“啟動”實現接口。

public class Rocket: IRocket
    {
        public string Name { get; set; }
        public string Model { get; set; }

        public void Launch(int delaySeconds)
        {

            Console.WriteLine(string.Format("Launching rocket in {0} seconds",delaySeconds));
            Thread.Sleep(1000 * delaySeconds);
            Console.WriteLine("Congratulations! You have successfully launched the rocket");
        }
    }

時間來創建我們的第一個攔截器。我們可以通過實現IInterceptor接口來實現。這是DynamicProxy將要使用的接口。

如下所示,我們正在登錄方法條目,調用執行實際方法的invocation.Proceed()方法,登錄成功執行登錄異常退出登錄。我們不必在其中編寫日志記錄代碼我們的業務模式了!我們只需要將LoggingInterceptor附加到需要記錄的組件上。

 

public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            var methodName = invocation.Method.Name;
            try
            {
                Console.WriteLine(string.Format("Entered Method:{0}, Arguments: {1}", methodName, string.Join(",", invocation.Arguments)));
                invocation.Proceed();
                Console.WriteLine(string.Format("Sucessfully executed method:{0}", methodName));
            }
            catch (Exception e)
            {
                Console.WriteLine(string.Format("Method:{0}, Exception:{1}", methodName, e.Message));
                throw;
            }
            finally
            {
                Console.WriteLine(string.Format("Exiting Method:{0}", methodName));
            }
        }

DynamicProxy公開IInvocation對象非常有用。它可以訪問當前的MethodInfo,Arguments,ReturnValue和許多其他細節,如下所示。

public interface IInvocation
    {
        object[] Arguments { get; }
        Type[] GenericArguments { get; }
        object InvocationTarget { get; }
        MethodInfo Method { get; }
        MethodInfo MethodInvocationTarget { get; }
        object Proxy { get; }
        object ReturnValue { get; set; }
        Type TargetType { get; }
        object GetArgumentValue(int index);
        MethodInfo GetConcreteMethod();
        MethodInfo GetConcreteMethodInvocationTarget();
        void Proceed();
        void SetArgumentValue(int index, object value);
    }

實施IRegistration界面並注冊您的組件。注冊攔截器后跟業務組件。指定要與每個業務組件一起使用的攔截器。您可能已經注意到,LoggingInterceptor附加到我們唯一的業務組件Rocket

public class ComponentRegistration : IRegistration
    {
        public void Register(IKernelInternal kernel)
        {
            kernel.Register(
                Component.For<LoggingInterceptor>()
                    .ImplementedBy<LoggingInterceptor>());

            kernel.Register(
                Component.For<IRocket>()
                         .ImplementedBy<Rocket>()
                         .Interceptors(InterceptorReference.ForType<LoggingInterceptor>()).Anywhere);
        }
    }

創建Windsor容器(IWindsorContainer)的靜態實例,使用組件注冊信息進行初始化。

public class DependencyResolver
    {
        private static IWindsorContainer _container;

        //Initialize the container
        public static void Initialize()
        {
            _container = new WindsorContainer();
            _container.Register(new ComponentRegistration());
        }

        //Resolve types
        public static T For<T>()
        {
            return _container.Resolve<T>();
        }
    }

用於運行我們的代碼的微型控制台應用程序。

 

 internal class Program
    {
        public  static void Main(string[] args)
        {
            //Initialize the dependency resolver
            DependencyResolver.Initialize();

            //resolve the type:Rocket
            var rocket = DependencyResolver.For<IRocket>();

            //method call
            try
            {
                rocket.Launch(5); 
            }
            catch (Exception ex)
            {

            }
            System.Console.ReadKey();
           
        }
    }

讓我們看看控制台輸出。正如你所期望的,我們的LoggingInterceptor攔截了方法調用和記錄的方法條目並自動退出。感謝DynamicProxy!

興趣點

這是一篇介紹性文章,讓初學者和中級開發人員使用Castle Windsor DynamicProxy了解AOP的基本概念在未來的日子里,我將繼續更新本文,並展示如何在Web Api項目中使用Log4net和DynamicProxy實現此解決方案。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM