首页 > 编程语言 >【代码设计】C# 实现 AOP 面向切面编程

【代码设计】C# 实现 AOP 面向切面编程

时间:2023-04-02 11:25:09浏览次数:27  
标签:C# Create void 切面 AOP invocation 执行 public

    简单记录一下对AOP的认识,正文为3个部分

    一、AOP由来

    二、用DispatchProxy动态代理实现AOP

    三、通过特性标记,处理多种不同执行前、执行后的逻辑编排

 

一、AOP 由来

    IUserHelper userHelper = new CommonUserHelper();
// 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(); } }

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的封装(可通过Nuget管理下载),

  通过实现 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);
// 对于 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); 
}; } } public abstract class BaseAOPAttribute : Attribute { public abstract Action<IInvocation> AOPAction(IInvocation invocation, Action<IInvocation> action); }

  通过给方法标记特性的方式,达到切面编程的目的(不影响原有实现,而增加实现执行前/执行后的逻辑)

    public interface IUserHelper
    {
        [LogBefore]
        [LogAfter]
        [CheckIP]
        void Create(string name);

        void CreateNoAttri();
    }

 

============================================================

具体的AOP实现上需要考虑的问题多如牛毛,此处仅做简单的思路介绍。

以上主要参考自 B站 朝夕教育 2022 .Net Core AOP实现。

 

标签:C#,Create,void,切面,AOP,invocation,执行,public
From: https://www.cnblogs.com/carmen-019/p/17280096.html

相关文章

  • Centos7 中 关于 tcp_syn_retries
    设置地址:/proc/sys/net/ipv4/tcp_syn_retries默认设置成6,代表在syn请求超时的情况下重发6次,每次的等待时间为2^times ,即2s,4s,8s,16s,32s,64s(不计最初的请求的1s)所以syn的超时时间为2^retries+1  ......
  • AcWing 1024. 装箱问题
    有一个箱子容量为V,同时有n个物品,每个物品有一个体积(正整数)。要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。输入格式第一行是一个整数V,表示箱子容量。第二行是一个整数n,表示物品数。接下来n行,每行一个正整数(不超过10000),分别表示这n个物品的各自体积。......
  • AcWing 8. 二维费用的背包问题
    有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。输出最大价值。输入格式第一行三个整数,N,V,M,......
  • AtCoder Beginner Contest 153
    AtCoderBeginnerContest153https://atcoder.jp/contests/abc153这套比较简单。E-CrestedIbisvsMonster完全背包#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintN=1e3+5,M=1e4+5;lln,m,a[N],b[N],f[M*2],mx;int......
  • [备忘] Linux 里 SVN 搭建步骤 (CentOS)
    第一步:安装SVNyuminstallsubversion-y第二步:创建版本库目录mkdir-p/home/svn第三步:创建SVN版本库svnadmincreate/home/svn/pro1这里pro1,可以改为自己需要的项目名称第四步:修改配置cd/home/svn/pro1/confauthz:配置权限文件passwd:配置账号密码文件svnser......
  • AcWing 9. 分组背包问题
    有N组物品和一个容量是V的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是vij,价值是wij,其中i是组号,j是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。输入格式第一行有两个整数N,V,用空格隔开,分别......
  • 域格CLM920模组使用pppd专网拨号
    域格CLM920模组使用pppd专网拨号参考资料参照《9X07模块LINUX集成用户手册版本_V1.60》的第四章节使用pppd拨号上网编写chat脚本chat-script要点:-使用AT+CGDCONT设置APN-使用AT$QCPDPP写鉴权-使用AT^NETCFG设置NDIS接口,注意^需要转义\^TIMEOUT15ABORT"DELAYED"A......
  • AcWing 3. 完全背包问题
    有N种物品和一个容量是V的背包,每种物品都有无限件可用。第i种物品的体积是vi,价值是wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。输入格式第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。接下来有N行,每行两个......
  • CNTT
    在oiwiki上看到了CNTT后,忍不住想试试。CNTT的原理:\(\mathbb{Z}_p^*\)->\(\mathbb{Z}_{p^2-1}^{\mathbb{C}*}\)(也不知道是不是这个符号,就这么看吧我们令\(j^2=x\),其中\(x\)是%p下的一个二次非剩余。由于我们只要做\(2^n\)长度的变换,\(x\)不用是原根。这时,在%p的复......
  • AcWing 4. 多重背包问题
    有N种物品和一个容量是V的背包。第i种物品最多有si件,每件体积是vi,价值是wi。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。输入格式第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。接下来有N行,每行三个整数vi,wi,......