前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# 实现 AOP 面向切面编程

C# 实现 AOP 面向切面编程

作者头像
郑子铭
发布2023-08-29 09:05:01
4100
发布2023-08-29 09:05:01
举报
文章被收录于专栏:DotNet NB && CloudNative

前言

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

AOP由来

用DispatchProxy动态代理实现AOP

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

一、AOP 由来

代码语言:javascript
复制
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种业务逻辑:

1、执行前写入日志,执行 Create

2、执行前写入日志,执行 Create,执行后写入日志

3、执行 Create,执行后写入日志

4、执行 Create

单一个写日志的需求,就能有4种实现方式,极端情况下,是可以实现 4次 Create 方法;

如果再加一个数据验证、IP验证、权限验证、异常处理、加入缓存..,那么实现的排列组合方式就更多了,

无穷尽地加实现、替换类,这显然不是我们希望的。

AOP,Aspect Oriented Programing,是一种编程思维,是对这种缺陷的补充。

二、DispatchProxy (动态代理)实现AOP

代码语言:javascript
复制
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以重写 执行前/执行后 逻辑的封装方式,我们可以更加聚焦在如何处理多种 执行前/执行后 逻辑的编排上。

代码语言:javascript
复制
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");
}    
代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
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);
}

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

代码语言:javascript
复制
public interface IUserHelper
{
    [LogBefore]
    [LogAfter]
    [CheckIP]
    void Create(string name);

    void CreateNoAttri();
}

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 三、通过标记特性,处理多种不同的执行前/执行后方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档