小暴 发表于 2023-4-2 10:12:07

【设计】C#如何实现AOP 面向切面编程

一、AOP 由来

    IUserHelper userHelper = new CommonUserHelper();<br>    // commonUser.Create中存在 方法执行前、方法执行后的业务逻辑
    userHelper.Create("test0401_A");


    public interface IUserHelper
    {
      void Create(string name);
    }

    public class CommonUserHelper : IUserHelper
    {
      private void before()
      {
            Console.WriteLine("CommonUser before");
      }

      private void after()
      {
            Console.WriteLine("CommonUser after");
      }

      public void Create(string name)
      {
            before();
            Console.WriteLine($"Common User : {name} Created !");
            after();
      }
    }
<br>CommonUserHelper 实现 IUserHelper 接口,假设希望在 Create方法执行前/后写入日志,那就存在这4种业务逻辑:
  ① 执行前写入日志,执行 Create
  ② 执行前写入日志,执行 Create,执行后写入日志
  ③ 执行 Create,执行后写入日志
  ④ 执行 Create
  单一个写日志的需求,就能有4种实现方式,极端情况下,是可以实现 4次 Create 方法;
  如果再加一个数据验证、IP验证、权限验证、异常处理、加入缓存..,那么实现的排列组合方式就更多了,
  无穷尽地加实现、替换类,这显然不是我们希望的。
AOP,Aspect Oriented Programing,是一种编程思维,是对这种缺陷的补充。
 
二、DispatchProxy (动态代理)实现AOP

using System.Reflection;
namespace Cjm.AOP
{
    public class TransformProxy
    {
      public static T GetDynamicProxy<T>(T instance)
      {
            // DispatchProxy 是system.Reflection封装的类
            // 用以创建实现接口T的代理类CustomProxy的实例
            dynamic obj = DispatchProxy.Create<T, CustomProxy<T>>();
            obj.Instance = instance;
            return (T)obj;
      }
    }

    // DispatchProxy 是抽象类,
    // 实现该类的实例,实例方法执行是会跳转到 Invoke 方法中,
    // 以此达到不破坏实际执行的具体逻辑,而又可以在另外的地方实现执行前、执行后
    public class CustomProxy<T> : DispatchProxy
    {
      public T Instance { get; set; }
      protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
      {
            BeforeProcess();
            var relt = targetMethod?.Invoke(Instance, args);
            AfterProcess();
            return relt;
      }

      private void BeforeProcess()
      {
            Console.WriteLine($"This is BegoreProcess.");
      }

      private void AfterProcess()
      {
            Console.WriteLine($"This is AfterProcess.");
      }
    }
}

    // Main
    IUserHelper userHelper3 = new CommonUserHelper();
    userHelper3 = TransformProxy.GetDynamicProxy(userHelper3);
    userHelper3.Create("test0401_B"); 
三、通过标记特性,处理多种不同的执行前/执行后方法

  此处借用Castle.Core的封装,
  通过实现 StandardInterceptor以重写 执行前/执行后 逻辑的封装方式,
  我们可以更加聚焦在如何处理多种 执行前/执行后 逻辑的编排上。
using Castle.DynamicProxy;
{
    ProxyGenerator proxy = new ProxyGenerator();
    CustomInterceptor customInterceptor = new CustomInterceptor();
    IUserHelper commonUserHelper = new CommonUserHelper();
    var userHelperProxy = proxy.CreateInterfaceProxyWithTarget<IUserHelper>(commonUserHelper, customInterceptor);
    userHelperProxy.Create("TEST0401_C");
}        public class CustomInterceptor : StandardInterceptor
    {
      protected override void PreProceed(IInvocation invocation)
      {
            var method = invocation.Method;
            //if (method.IsDefined(typeof(LogBeforeAttribute), true))
            //{
            //    Console.WriteLine("LOG : CustomInterceptor.PreProceed");
            //}

            Action<IInvocation> action = (invocation) => base.PreProceed(invocation);
            // 获取该方法的所有继承BaseAOPAttribute的特性
            var attrs = method.GetCustomAttributes<BaseAOPAttribute>(true);<br>            // 对于 attrs 的排列顺序,可以在特性的实现中增加 int order 属性,在标记特性时写入排序编号
            foreach(var attr in attrs)
            {
                // 这里是俄罗斯套娃
                // 相当于 attr3.AOPAction(invocation, attr2.AOPAction(invocation, attr1.AOPAction(invocation, base.PreProceed(invocation))))
                action = attr.AOPAction(invocation, action);
            }
            action.Invoke(invocation);
      }

      protected override void PerformProceed(IInvocation invocation)
      {
            Console.WriteLine("CustomInterceptor.PerformProceed");
            base.PerformProceed(invocation);
      }

      protected override void PostProceed(IInvocation invocation)
      {
            var method = invocation.Method;
            if (method.IsDefined(typeof(LogAfterAttribute), true))
            {
                Console.WriteLine("LOG : CustomInterceptor.PostProceed");
            }

            base.PreProceed(invocation);
      }
    }    public class LogBeforeAttribute : Attribute {}

    public class LogAfterAttribute : Attribute {}

    public class CheckIPAttribute : BaseAOPAttribute
    {
      public override Action<IInvocation> AOPAction(IInvocation invocation, Action<IInvocation> action)
      {
            return (invocation) => {
                Console.WriteLine("CheckIP ..");
                action.Invoke(invocation); <br>            };
      }
    }

    public abstract class BaseAOPAttribute : Attribute
    {
      public abstract Action<IInvocation> AOPAction(IInvocation invocation, Action<IInvocation> action);
    }  通过给方法标记特性的方式,达到切面编程的目的(不影响原有实现,而增加实现执行前/执行后的逻辑)
    public interface IUserHelper
    {
      
      
      
      void Create(string name);

      void CreateNoAttri();
    } 

来源:https://www.cnblogs.com/carmen-019/archive/2023/04/02/17280096.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 【设计】C#如何实现AOP 面向切面编程