.NET Core 利用委托进行动态流程组装
引言
在看.NET Core 源码的管道模型中间件(Middleware)部分,觉得这个流程组装,思路挺好的,于是就分享给大家。本次代码实现就直接我之前写的动态代理实现AOP的基础上直接改了,就不另起炉灶了,主要思路就是运用委托。对委托不理解的可留言,我写一篇委托的常规使用方式,以及底层原理(编译器)的文章

没看过上一章的,我这里大家给贴一下地址:.NET Core 实现动态代理做AOP(面向切面编程) - 极客Bob - 博客园 (cnblogs.com)
接下来进入正题
1.定义接口IInterceptor
定义好我们AOP需要实现的接口,不同职责可以定义不同接口,大家根据实际情况划分
查看代码
internal interface IInterceptor
{
}
internal interface IInterceptorAction : IInterceptor
{
/// <summary>
/// 执行之前
/// </summary>
/// <param name="args">参数</param>
void AfterAction(object?[]? args);
/// <summary>
/// 执行之后
/// </summary>
/// <param name="args">参数</param>
/// <param name="result">结果</param>
void BeforeAction(object?[]? args, object? result);
}
2.定义特性
这里只定义一个基类特性类,继承标记接口,用于设置共通配置,且利于后面反射查找
查看代码
[AttributeUsage(AttributeTargets.Class)]
internal class BaseInterceptAttribute : Attribute, IInterceptor
{
}
3.编写生成代理类的逻辑
只需要继承.NET CORE 原生DispatchProxy类,重写相关业务代码
3.1 编写创建代理方法
编写一个我们自己的Create方法(),这两个参数为了后面调用目标类储备的,方法实现就只需要调用DispatchProxy类的Create()
查看代码
internal class DynamicProxy<T> : DispatchProxy
{
public T Decorated { get; set; }//目标类
public IEnumerable<IInterceptorAction> Interceptors { get; set; } // AOP动作
/// <summary>
/// 创建代理实例
/// </summary>
/// <param name="decorated">代理的接口类型</param>
/// <param name="afterAction">方法执行前执行的事件</param>
/// <param name="beforeAction">方法执行后执行的事件</param>
/// <returns></returns>
public T Create(T decorated, IEnumerable<IInterceptor> interceptors)
{
object proxy = Create<T, DynamicProxy<T>>(); // 调用DispatchProxy 的Create 创建一个代理实例
DynamicProxy<T> proxyDecorator = (DynamicProxy<T>)proxy;
proxyDecorator.Decorated = decorated;
proxyDecorator.Interceptors = interceptors.Where(c=>c.GetType().GetInterface(typeof(IInterceptorAction).Name) == typeof(IInterceptorAction)).Select(c=>c as IInterceptorAction);
return (T)proxy;
}
3.2 重写Invoke方法
这个就是需要实现我们自己的业务了,大家看注释应该就能看懂个大概了,目前这里只处理了IInterceptorAction接口逻辑,比如异常、异步等等,自己可按需实现。而流程组装的精髓就三步
1.不直接去执行targetMethod.Invoke(),而是把它放到委托里面。
2.定义AssembleAction()方法来组装流程,方法里面也不执行方法,也是返回一个执行方法的委托。
3.循环事先在Create()方法存储的特性实例,调用AssembleAction()方法组装流程,这样就达到俄罗斯套娃那种效果了。
查看代码
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
Exception exception = null;//由委托捕获变量,用来存储异常
Func<object?[]?, object?> action = (args) =>
{
try
{
return targetMethod?.Invoke(Decorated, args);
}
catch (Exception ex)//捕获异常,不影响AOP继续执行
{
exception = ex;
}
return null;
};
//进行倒序,使其按照由外置内的流程执行
foreach (var c in Interceptors.Reverse())
{
action = AssembleAction(action, c);
}
//执行组装好的流程
var result = action?.Invoke(args);
//如果方法有异常抛出异常
if (exception != null)
{
throw exception;
}
return result;
}
private Func<object?[]?, object?>? AssembleAction(Func<object?[]?, object?>? action, IInterceptorAction c)
{
return (args) =>
{
//执行之前的动作
AfterAction(c.AfterAction, args);
var result = action?.Invoke(args);
//执行之后的动作
BeforeAction(c.BeforeAction, args, result);
return result;
};
}
private void AfterAction(Action<object?[]?> action, object?[]? args)
{
try
{
action(args);
}
catch (Exception ex)
{
Console.WriteLine($"执行之前异常:{ex.Message},{ex.StackTrace}");
}
}
private void BeforeAction(Action<object?[]?, object?> action, object?[]? args, object? result)
{
try
{
action(args, result);
}
catch (Exception ex)
{
Console.WriteLine($"执行之后异常:{ex.Message},{ex.StackTrace}");
}
}
}
4.定义一个工厂
工厂用于专门来为我们创建代理类,逻辑很简单,后续大家也可以按需编写,目前逻辑就是利用反射获取目标类的特性,把参数组装起来。
查看代码
internal class ProxyFactory
{
/// <summary>
/// 创建代理实例
/// </summary>
/// <param name="decorated">代理的接口类型</param>
/// <returns></returns>
public static T Create<T>()
{
var decorated = ServiceHelp.GetService<T>();
var type = decorated.GetType();
var interceptAttribut = type.GetCustomAttributes<BaseInterceptAttribute>();
//创建代理类
var proxy = new DynamicProxy<T>().Create(decorated, interceptAttribut);
return proxy;
}
}
5.定义ServiceHelp
这个是为了使得我们全局只用一个作用域的IOC容器
查看代码
public static class ServiceHelp
{
public static IServiceProvider? serviceProvider { get; set; }
public static void BuildServiceProvider(IServiceCollection serviceCollection)
{
//构建容器
serviceProvider = serviceCollection.BuildServiceProvider();
}
public static T GetService<T>(Type serviceType)
{
return (T)serviceProvider.GetService(serviceType);
}
public static T GetService<T>()
{
return serviceProvider.GetService<T>();
}
}
6.测试
6.1 编写测试服务
写一个简单的测试服务,就比如两个整数相加
查看代码
internal interface ITestService
{
public int Add(int a, int b);
}
[AOPTest2Attribut]
[AOPTest1Attribut]
internal class TestService : ITestService
{
public int Add(int a, int b)
{
Console.WriteLine($"正在执行--》Add({a},{b})");
//throw new Exception("方法执行--》测试异常");
return a + b;
}
}
6.2编程AOP实现
写两个特性实现,继承基类特性,实现Action接口逻辑,测试两个特性随意调换位置进行组装流程
查看代码
internal class AOPTest1Attribut : BaseInterceptAttribute, IInterceptorAction
{
public void AfterAction(object?[]? args)
{
Console.WriteLine($"AOP1方法执行之前,args:{args[0] + "," + args[1]}");
// throw new Exception("异常测试(异常,但依然不能影响程序执行)");
}
public void BeforeAction(object?[]? args, object? result)
{
Console.WriteLine($"AOP1方法执行之后,result:{result}");
}
}
internal class AOPTest2Attribut : BaseInterceptAttribute, IInterceptorAction
{
public void AfterAction(object?[]? args)
{
Console.WriteLine($"AOP2方法执行之前,args:{args[0] + "," + args[1]}");
}
public void BeforeAction(object?[]? args, object? result)
{
Console.WriteLine($"AOP2方法执行之后,result:{result}");
}
}
6.3 调用
1.把服务注册到IOC
2.调用创建代理类的工厂
查看代码
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ITestService, TestService>();
ServiceHelp.BuildServiceProvider(serviceCollection);
//用工厂获取代理实例
var s = ProxyFactory.Create<ITestService>();
var sum = s.Add(1, 2);
6.4 效果图
AOP1->AOP2->Add(a,b)

AOP2->AOP1->Add(a,b)

代码上传至gitee,AOP流程组装分支:https://gitee.com/luoxiangbao/dynamic-proxy.git
.NET Core 利用委托进行动态流程组装的更多相关文章
- 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?
在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...
- Core管道中的处理流程3
通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的? 在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下 ...
- [源码解析] 并行分布式任务队列 Celery 之 消费动态流程
[源码解析] 并行分布式任务队列 Celery 之 消费动态流程 目录 [源码解析] 并行分布式任务队列 Celery 之 消费动态流程 0x00 摘要 0x01 来由 0x02 逻辑 in komb ...
- 代理模式:利用JDK原生动态实现AOP
代理模式:利用JDK原生动态实现AOP http://www.cnblogs.com/qiuyong/p/6412870.html 1.概述 含义:控制对对象的访问. 作用:详细控制某个(某类)某对象 ...
- iOS 利用 Framework 进行动态更新
http://nixwang.com/2015/11/09/ios-dynamic-update/ 前言 目前 iOS 上的动态更新方案主要有以下 4 种: HTML 5 lua(wax)hotpat ...
- C#利用委托跨线程更新UI数据
转:http://www.2cto.com/kf/201206/136587.html 在使用C#的过程中,难免会用到多线程,而用多线程之后,线程如何与界面交互则是一个非常头疼的问题.其实不仅仅是界面 ...
- 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)
原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...
- winform利用委托delegate进行窗体间通信
前段时间学习委托,感觉很模糊的样子,也做过许多实例,但是项目中一直没有用到,今天在项目中遇到一个很简单的例子,现在拿出来,做一个简单的记录. 要求:将弹出框里勾选的内容返回到主面板上. 工具:委托. ...
- winform利用委托delegate进行窗体间通信,相同标题已经存在??
前段时间学习委托,感觉很模糊的样子,也做过许多实例,但是项目中一直没有用到,今天在项目中遇到一个很简单的例子,现在拿出来,做一个简单的记录. 要求:将弹出框里勾选的内容返回到主面板上. 工具:委托. ...
随机推荐
- N1BOOK——[第五章 CTF之RE章]wp
推荐在了解了相应章节的内容后再来练习,你会觉得顿时悟了 记录一下自己的解题过程 2,3,4题目附件来源:https://book.nu1l.com/tasks/#/pages/reverse/5.4 ...
- .NET Core工程应用系列(2) 实现可配置Attribute的Json序列化方案
背景 在这篇文章中,我们实现了基于自定义Attribute的审计日志数据对象属性过滤,但是在实际项目的应用中遇到了一点麻烦.需要进行审计的对象属性中会包含其他类对象,而我们之前的实现是没办法处理这种类 ...
- vscode提示提示安装似乎损坏,请重新安装
安装 Fix VSCode Checksums 插件 Ctrl+Shift+P , 输入Fix Checksums : Apply, 点击,重启即可
- java 多线程 线程组ThreadGroup;多线程的异常处理。interrupt批量停止组内线程;线程组异常处理
1,线程组定义: 线程组存在的意义,首要原因是安全.java默认创建的线程都是属于系统线程组,而同一个线程组的线程是可以相互修改对方的数据的.但如果在不同的线程组中,那么就不能"跨线程组&q ...
- CentOS7.6 鲜为人知的/etc/resolv.conf 之 /etc/resolv.conf.save (保持/etc/resolv.conf不被修改:/etc/dhcp/dhclient-enter-hooks 无效之/etc/resolv.conf被清空的特殊案例)
目的: 用户可以自定义/etc/resolv.conf内容,且不被系统修改. 常规方法1: /etc/sysconfig/network-scripts/ifcfg-eth0 网卡配置文件中增加PEE ...
- 基于nginx的rtmp直播服务器(nginx-rtmp-module实现)
首先,在搭建服务之前先了解下目前主流的几个直播协议: 1.RTMP: 实时消息传输协议,Real Time Messaging Protocol,是 Adobe Systems 公司为 Flash 播 ...
- [C# Expression] 之基础概念
00 | 什么是表达式树 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等.可以对表达式树中的代码进行编辑和运算. 这样能够动态修改可执 ...
- ThreadLocal的正确使用与原理
ThreadLocal是什么 ThreadLocal是线程Thread中属性threadLocals即ThreadLocal.ThreadLocalMap的管理者,ThreadLocal用于给每个线程 ...
- 【LeetCode】235. Lowest Common Ancestor of a Binary Search Tree 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 [LeetCode] https://leet ...
- 【九度OJ】题目1023:EXCEL排序 解题报告
[九度OJ]题目1023:EXCEL排序 解题报告 标签(空格分隔): 九度OJ [LeetCode] http://ac.jobdu.com/problem.php?pid=1023 题目描述: E ...