c#中AOP面向切面编程架构思想具体实现,使用Unity动态代理生成拦截器!

523 阅读5分钟

一、AOP面向切面编程思想概述:

在C#中,AOP(Aspect Oriented Programming,面向切面编程)是一种编程模式,它允许程序员在运行时将代码注入到应用程序中,以实现横切关注点的统一管理,例如日志记录、异常处理、性能统计等。

二、实现方式

C#中实现AOP的方式通常是基于反射和委托的,主要有以下两种常见方式:

1. 使用Attribute实现AOP

C#内置特性Attribute,可以在方法或类上自定义特性,并在特性中执行一些预先定义好的动作。这种方式需要通过反射技术来查找并执行特性中的代码。

2. 使用动态代理实现AOP

动态代理是指在运行时生成代理对象以代替实际对象执行方法。使用动态代理可以通过委托实现预定义的动作,例如记录日志、处理异常等。

C#中还有一些第三方AOP框架,例如PostSharp、Castle Windsor等,它们提供了更强大的AOP支持,可以支持更复杂的剖面(Aspect)和更多的动作(Action)类型。

使用AOP可以让代码更加简洁、易于维护和扩展,同时也提高了程序的性能和可读性,是一种非常值得掌握的编程思想。

三、使用Unity动态代理实现AOP

今天我们这里就简单的讲述一下,使用Unity动态代理实现AOP。

1、引入管理Nuget包(Unity.Abstractions、Unity.Configuration、Unity.Container、Unity.Interception、Unity.Interception.Configuration)

image.png

2、简单业务叙述

我有一个商品接口查询商品的接口,在用户查询商品接口之前、我需要判断用户是否登录,只有登录了才能查看商品,同时校验代码是否有报错异常.。....大家还可以根据自己的思想添加不同业务逻辑,可扩展。

3、代码配置演示

/// <summary>
/// 查询商品接口
/// </summary>
public interface IProduct
{
    void GetProduct(Users users);
}

/// <summary>
/// 接口实现
/// </summary>
public class Product : IProduct
{
    /// <summary>
    /// 查询商品
    /// </summary>
    /// <param name="users"></param>
    /// <exception cref="NotImplementedException"></exception>
    public void GetProduct(Users users)
    {
        Console.WriteLine("我是用户登录之后查询到的商品: A");
    }
}

/// <summary>
/// 用户类
/// </summary>
public class Users
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }
}

 /// <summary>
/// Unity实现AOP
/// 固定写法,复制粘贴即可
/// </summary>
public class UtityConfigAOP
{
    /// <summary> 
    /// Unity实现AOP
    /// 固定写法,复制粘贴即可
    /// 参数根据自己的业务逻辑可变更
    /// </summary>
    /// <param name="users">用户</param>
    public static void Find(Users users)
    {
        //配置IUnityContainer信息
        IUnityContainer container = new UnityContainer();
        ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
        //读取Unity.Config文件地址
        //AOP_Unity_Config\\Unity.Config配置文件的地址,代码生成后 bin目录下的地址
        fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "AOP_Unity_Config\\Unity.Config");
        /**
         *打开给定映射文件的exe.config文件。
         *用于动态修改应用程序的配置文件,
         *或者在运行时加载另一个配置文件。
         *它接受一个ConfigurationFileMap参数,
         *该参数描述了可访问的配置文件以及它们的位置。
         *该方法返回一个Configuration对象,该对象表示指定配置文件的完整内容。
         */
        Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        /**
         * UnityConfigurationSection 是Unity IOC容器提供的配置节。
         * 它负责加载和解析应用程序的配置文件中的Unity IOC配置节,并创建容器实例,以便在应用程序中进行依赖注入操作。
         * 配置节是应用程序配置文件(例如app.config或web.config)中的一个特殊部分,它允许您指定应用程序的配置信息。
         * UnityConfigurationSection是在应用程序配置文件中指定Unity容器的信息的部分。
         * 它定义了如何注册和构造类,并描述了容器应如何配置各种对象的行为。
         * UnityConfigurationSection使得在应用程序中实现依赖注入更加容易,
         * 因为它提供了一种简单的方式来配置Unity容器,而不必在代码中编写依赖注入的配置。
         * 
         * 简单的来说:这一行就是去config文件种找unity名称的配置信息,进行读取
         */
        UnityConfigurationSection configurationSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
        //AopContainer 自己定义的配置文件节段名称
        configurationSection.Configure(container, "AopContainer");


        //对IProduct接口进行AOP配置
        //大家也可以根据自己的业务逻辑进行变更,因为此处我是对查询商品接口进行验证,所以Resolve<IProduct>
        IProduct product = container.Resolve<IProduct>();
        product.GetProduct(users);

    }
}

<!--这是我的Unity.Config配置文件-->
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
  </configSections>
  <unity>
    <sectionExtension type ="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
    <containers><!--容器-->
      <container name="AopContainer">
        <extension type ="Interception" />
        <register type ="YDTFileTest.AOP.IProduct,YDTFileTest"
                  mapTo="YDTFileTest.AOP.Product,YDTFileTest">
          <interceptor type ="InterfaceInterceptor"/>
            <!--参数校验-->
          <interceptionBehavior type ="YDTFileTest.AOP.ParameterCheckBehavior, YDTFileTest"/>
           <!--代码异常校验-->
          <interceptionBehavior type="YDTFileTest.AOP.ExceptionLoggingBehavior, YDTFileTest"/>
          
        </register>
      </container>
    </containers>
  </unity>
</configuration>


/// <summary>
/// 参数校验类,必须继承IInterceptionBehavior
/// </summary>
public class ParameterCheckBehavior : IInterceptionBehavior
{
    /// <summary>
    /// 固定写法不用管,继承IInterceptionBehavior 自动生成的
    /// </summary>
    /// <returns></returns>
    public bool WillExecute { get { return true; } }

    /// <summary>
    /// 固定写法不用管,继承IInterceptionBehavior 自动生成的
    /// </summary>
    /// <returns></returns>
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    /// <summary>
    /// 只需要在这个方法中书写逻辑, 继承IInterceptionBehavior 自动生成的
    /// </summary>
    /// <param name="input"></param>
    /// <param name="getNext"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("开始参数校验");
        Users user = input.Inputs[0] as Users;
        if (user.Name != "小王" || user.Password != "123")
        {
            Console.WriteLine("用户名密码不正确");
            //创建一个异常信息并抛出
            return input.CreateExceptionMethodReturn(new Exception("用户名密码不正确"));
        }
        else
        {
            Console.WriteLine("参数检测无误");
            //执行config中下下一个AOP配置校验
            return getNext().Invoke(input, getNext);
        }
    }
    
/// <summary>
/// 代码异常校验具体实现,必须继承IInterceptionBehavior
/// </summary>
public class ExceptionLoggingBehavior : IInterceptionBehavior
{
    /// <summary>
    /// 固定写法不用管,继承IInterceptionBehavior 自动生成的
    /// </summary>
    /// <returns></returns>
    public bool WillExecute { get { return true; } }

    /// <summary>
    /// 固定写法不用管,继承IInterceptionBehavior 自动生成的
    /// </summary>
    /// <returns></returns>
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    /// <summary>
    /// 只需要在这个方法中书写逻辑, 继承IInterceptionBehavior 自动生成的
    /// </summary>
    /// <param name="input"></param>
    /// <param name="getNext"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.WriteLine("开始异常校验");
        IMethodReturn methodReturn = getNext()(input, getNext);
        if (methodReturn.Exception == null)
        {
            Console.WriteLine("无异常");
        }
        else
        {
            Console.WriteLine($"异常:{methodReturn.Exception.Message}");
        }
        return methodReturn;
    }

4、代码调用演示

 //直接调用配置的Find方法进行验证,通过则自动记录日志等,并调用查看商品接口
 public  static void Load()
 {
     Users user = new Users()
     {
         Id = "1",
         Name = "小王",
         Password = "123",
     };

     UtityConfigAOP.Find(user);
 }
 
 

image.png 如上图,可以看到我们已经成功的实现了AOP动态代理拦截器的生成了,如果后续还需要添加什么验证,只需要在配置文件添加<interceptionBehavior type="文件所在项目路径, 项目名称"/>,并跟上面登录、异常验证一样只需继承IInterceptionBehavior并实现,在Invoke方法中写自己需要的逻辑即可,非常的方便,并且不会影响到之前的代码逻辑!

今天的分享就到这里了,大家还有更好的实现AOP的方式,欢迎到评论区讨论!!!