.NET中三种AOP实现方式:Remoting、Castle、Autofac

.NET中三种AOP实现方式

  • 前言
    • 基本概念
      • AOP
      • IOC和DI
      • 实现方式
        • 动态代理
        • 静态代理
  • Remoting实现方式
  • Castle实现方式
    • 依赖注入的实现
    • 添加拦截器
    • 完整代码
  • Autofac实现方式
    • 依赖注入的实现
    • 添加拦截器
    • 完整代码
  • 后记

前言

一直忙于撸码,难得有时间,研究了下AOP,与大家分享一下,也便于以后查看。( ̄▽ ̄)~*
因为.NET CORE中集成的IOC容器是Castle,日常大家常用的是Autofac,所以本人只研究了下.NET默认的Remoting实现方式,Castle实现方式和Autofac实现方式。其中,Castle有两个插件,Castle.Windsor(IOC容器)和Castle.Core(核心),Autofac的拦截器是使用的Castle.Core的动态代理。

基本概念

AOP

AOP(Aspect Oriented Programming):面向切面(方面)编程,是OOP的延续,用来在不修改代码的情况下,动态的添加功能,将核心逻辑与非核心逻辑分开。其中,记录日志便是AOP最普遍的使用场景之一。

IOC和DI

DI(Dependency Injection)依赖注入,实际上是IOC(控制翻转)的一种实现方式,之所以叫依赖注入,是因为调用者和被调用者的依赖关系是通过第三方注入的,这个第三方通常说的便是IOC容器。IOC是一种思想,依赖倒置原则提倡程序要依赖于抽象,不要依赖于具体实现,以便降低耦合,而控制实现接口的,通常还是调用者,调用者和具体实现之间还是存在依赖关系,现在把这个控制权交给第三方,这便是IOC思想。在这里讲这些内容,是因为实现动态代理,往往需要依托于IOC容器。

实现方式

AOP的拦截器有两种实现方式:动态代理和静态代理

动态代理

是在程序运行中,动态的添加切入点,这便是通常需要用到IOC容器的原因。
优缺点:效率相对于静态代理要低,但灵活性强,不需要额外的编译。
我们所讲的三种方式,都是动态代理实现的。

静态代理

是在程序预编译阶段插进去的。
优缺点:相对于动态代理,性能要高,但灵活性较差,需要额外的编译。

Remoting实现方式

  1. 需要引用的命名空间
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
  1. 因为实现比较简单,所以就把代码全部贴到下面了
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Text;
using System.Threading.Tasks;

/// 
/// 默认实现方式(Remoting方式)
/// 
namespace DefaultDemo
{

    class Program
    {
        static void Main(string[] args)
        {
            var per = new Proxy(new King()).GetTransparentProxy() as King;
            if (per != null)
            {
                var str = per.RuleTheCastle("fffff");
            }
            Console.ReadKey();
        }
    }


    /// 
    /// 基本类型
    /// 
    class King : MarshalByRefObject
    {
        public string RuleTheCastle(string str)
        {
            str = "hello word," + str;
            return str;
        }
    }


    public class Proxy : RealProxy where T : new()
    {
        private object _obj;
        public Proxy(object obj)
            : base(typeof(T))
        {
            _obj = obj;
        }
        public override IMessage Invoke(IMessage msg)
        {
            IDictionary oProperties = msg.Properties;

            object oMethodName = null;
            if (oProperties.Contains("__MethodName"))
            {
                oMethodName = oProperties["__MethodName"];
            }
            Console.WriteLine("方法名:{0}", oMethodName);

            //入参
            object[] oArguments;
            if (oProperties.Contains("__Args"))
            {
                oArguments = oProperties["__Args"] as object[];
            }
            else
            {
                oArguments = new object[0];
            }
            foreach (var oArgument in oArguments)
            {
                Console.WriteLine("入参:{0}", oArgument);
            }


            //调用和出参
            object oReturnValue = ((IMethodCallMessage)msg).MethodBase.Invoke(_obj, oArguments);
            Console.WriteLine("出参:{0}", oReturnValue);

            return new ReturnMessage(oReturnValue, null, 0, null, null);
        }
    }

}

Castle实现方式

  1. 在NuGet管理器中安装Castle.Windsor程序包
    .NET中三种AOP实现方式:Remoting、Castle、Autofac_第1张图片
    安装完成后会自动引用Castle.Windsor和Castle.Core类库
    .NET中三种AOP实现方式:Remoting、Castle、Autofac_第2张图片
  2. 引用对应的命名空间
using Castle.Core;
using Castle.DynamicProxy;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

依赖注入的实现

  1. 添加一个抽象接口,和这个抽象接口的实现类
	public interface IKing
    {
        string RuleTheCastle(string name);
    }


    class King : IKing
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public string RuleTheCastle(string name)
        {
            Console.WriteLine("方法已执行");
            return "Hello Word," + name;
        }
    }

2、添加一个安装类RepositoriesInstaller ,需要继承IWindsorInstaller接口,在里面注册组件

    public class RepositoriesInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For().ImplementedBy().LifestyleSingleton()
                );
        }
    }
  1. 实例化一个容器,并运行安装程序,以便它们在容器中注册组件
	IWindsorContainer container = new WindsorContainer();

    container.Install(new RepositoriesInstaller());
  1. 获取组件实例,依赖注入
	var king = container.Resolve();
	string ret = king.RuleTheCastle("tzj");
  1. 释放掉IOC容器
	container.Dispose();

添加拦截器

  1. 声明一个日志的拦截器类LoggingInterceptor,并让他继承IInterceptor接口
 public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                //类型
                var type = invocation.TargetType;

                //对象
                var oObject = invocation.InvocationTarget;

                //方法
                string strMethodName = invocation.Method.Name;
                Console.WriteLine("调用的方法是:" + strMethodName);

                //入参
                var oArguments = invocation.Arguments;
                if (oArguments != null && oArguments.Length > 0)
                {
                    for (int i = 0; i < oArguments.Length; i++)
                    {
                        Console.WriteLine("入参{0}:{1}", i, oArguments[i]);
                    }

                }

                //执行此方法,正式调用方法
                //执行之前,可获取入参
                //执行成功之后,可获取出参
                invocation.Proceed();

                //输出参数和ref只能通过对比入参来实现

                //出参
                var oReturnValue = invocation.ReturnValue;
                Console.WriteLine("出参:" + oReturnValue);
            }
            catch (Exception ex)
            {
                //不管是被拦截方法中报错,还是拦截方法中报错,都会走这里
                Console.WriteLine("错误信息:" + ex.ToString());
            }
        }
    }

2.在实现类上添加对应的拦截器属性,需要注意的是,这个实现类必须是受本IOC容器控制的

[Interceptor(typeof(LoggingInterceptor))]
  1. 将拦截器也注册的容器中
 Component.For()

这样便完成了

完整代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Castle.Core;
using Castle.DynamicProxy;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

namespace CastleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            IWindsorContainer container = new WindsorContainer();

            container.Install(new RepositoriesInstaller());

            var king = container.Resolve();

            string ret = king.RuleTheCastle("tzj");
            Console.WriteLine(ret);

            container.Dispose();

            Console.ReadKey();
        }
    }

    public interface IKing
    {
        string RuleTheCastle(string name);
    }

    //切面的类必须是受本容器控制的
    [Interceptor(typeof(LoggingInterceptor))]
    class King : IKing
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public string RuleTheCastle(string name)
        {
            Console.WriteLine("方法已执行");
            return "Hello Word," + name;
        }
    }

    public class RepositoriesInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For(),
                Component.For().ImplementedBy().LifestyleSingleton()
                );
        }
    }


    public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                //类型
                var type = invocation.TargetType;

                //对象
                var oObject = invocation.InvocationTarget;

                //方法
                string strMethodName = invocation.Method.Name;
                Console.WriteLine("调用的方法是:" + strMethodName);

                //入参
                var oArguments = invocation.Arguments;
                if (oArguments != null && oArguments.Length > 0)
                {
                    for (int i = 0; i < oArguments.Length; i++)
                    {
                        Console.WriteLine("入参{0}:{1}", i, oArguments[i]);
                    }

                }

                //执行此方法,正式调用方法
                //执行之前,可获取入参
                //执行成功之后,可获取出参
                invocation.Proceed();

                //输出参数和ref只能通过对比入参来实现

                //出参
                var oReturnValue = invocation.ReturnValue;
                Console.WriteLine("出参:" + oReturnValue);
            }
            catch (Exception ex)
            {
                //不管是被拦截方法中报错,还是拦截方法中报错,都会走这里
                Console.WriteLine("错误信息:" + ex.ToString());
            }
        }
    }
}

Autofac实现方式

  1. 在NuGet管理器中安装Autofac.Extras.DynamicProxy程序包
    .NET中三种AOP实现方式:Remoting、Castle、Autofac_第3张图片
    程序会自动引用
    在这里插入图片描述
  2. 引用所需的命名空间
using Autofac;
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;

依赖注入的实现

这块与上面类似,直接贴代码了

private static IContainer Container { get; set; }
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            
            builder.RegisterType().As();

            Container = builder.Build();

            using (var scope = Container.BeginLifetimeScope())
            {
                var writer = scope.Resolve();

                writer.RuleTheCastle("aaa");
            }

            Console.Read();
        }

    }

    public interface IKing
    {
        string RuleTheCastle(string name);
    }

    public class King : IKing
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public string RuleTheCastle(string name)
        {
            Console.WriteLine("方法已执行");
            return "Hello Word," + name;
        }

    }

添加拦截器

拦截器也类似

  1. 添加拦截器属性
[Intercept(typeof(LoggingInterceptor))]
  1. 在注册组件的时候要启用代理

启用类代理,这种方式需要切面类的权限是public,并且方法是virtual方法

builder.RegisterType().As().EnableClassInterceptors();

启用接口代理

builder.RegisterType().As().EnableInterfaceInterceptors();
  1. 同样需要将拦截器注入到容器中
builder.RegisterType();

完整代码

using Autofac;
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AutofacDemo
{
    class Program
    {
        private static IContainer Container { get; set; }
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();

            builder.RegisterType();

            //EnableClassInterceptors 启用类代理 要求方法是virtual
            //builder.RegisterType().As().EnableClassInterceptors();

            //EnableInterfaceInterceptors 启用接口代理
            builder.RegisterType().As().EnableInterfaceInterceptors();

            Container = builder.Build();

            using (var scope = Container.BeginLifetimeScope())
            {
                var writer = scope.Resolve();

                writer.RuleTheCastle("aaa");
            }

            Console.Read();
        }

    }

    public interface IKing
    {
        string RuleTheCastle(string name);
    }

    //切面的类必须是受本容器控制的
    [Intercept(typeof(LoggingInterceptor))]
    public class King : IKing
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public string RuleTheCastle(string name)
        {
            Console.WriteLine("方法已执行");
            return "Hello Word," + name;
        }

    }

    public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                //类型
                var type = invocation.TargetType;

                //对象
                var oObject = invocation.InvocationTarget;

                //方法
                string strMethodName = invocation.Method.Name;
                Console.WriteLine("调用的方法是:" + strMethodName);

                //入参
                var oArguments = invocation.Arguments;
                if (oArguments != null && oArguments.Length > 0)
                {
                    for (int i = 0; i < oArguments.Length; i++)
                    {
                        Console.WriteLine("入参{0}:{1}", i, oArguments[i]);
                    }

                }

                //执行此方法,正式调用方法
                //执行之前,可获取入参
                //执行成功之后,可获取出参
                invocation.Proceed();

                //输出参数和ref只能通过对比入参来实现


                //出参
                var oReturnValue = invocation.ReturnValue;
                Console.WriteLine("出参:" + oReturnValue);


            }
            catch (Exception ex)
            {
                //不管是被拦截方法中报错,还是拦截方法中报错,都会走这里
                Console.WriteLine("错误信息:" + ex.ToString());
            }
        }
    }
}

后记

文章中,有些地方没写那么细,不过注释中大部分都有,等我有时间再往细加,还望见谅。(✪ω✪)
第一次正式写博客,如果哪有问题,还请大佬们不惜赐教。(〃‘▽’〃)

你可能感兴趣的:(AOP,.NET)