引言

在看.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

最新文章

  1. Linux超快速安装Ruby on Rails
  2. Navicat for MySQL安装之后不知道登录密码
  3. java web(一) 使用sql标签库+tomcat+mysql手动创建一个jsp练习总结
  4. bzoj题解汇总(1052~1061)
  5. BZOJ-1002 轮状病毒 高精度加减+Kirchhoff矩阵数定理+递推
  6. XE7 - 程序图标及启动画面图片的注意事项
  7. ubuntu鼠标突然不能使用的解决方法
  8. (转).NET技术+25台服务器怎样支撑世界第54大网站
  9. OpenStackCLI调试及术语识记
  10. 在hadoop 的任务中设置 map数量
  11. 源码篇:SDWebImage
  12. C# linq左连接与分组
  13. C语言程序设计 第一次作业
  14. SharePoint2013 列表栏设置
  15. 事务,Oracle,MySQL及Spring事务隔离级别
  16. 【从零开始自制CPU之学习篇02】555定时器
  17. 清北合肥day1
  18. Python的二叉树实现
  19. 在集群上运行Spark
  20. java基础基础总结----- 数组深入理解(四)

热门文章

  1. ios self.和_区别
  2. signal 信号
  3. my.ini配置文件内容
  4. Linux报错:ERROR&gt;the input device is not a TTY
  5. C++字符串常量跨平台编译问题
  6. 【LeetCode】1417. 重新格式化字符串 Reformat The String
  7. 【LeetCode】面试题 17.16. 按摩师 解题报告(C++)
  8. 【LeetCode】 204. Count Primes 解题报告(Python & C++)
  9. 【LeetCode】61. Rotate List 解题报告(Python)
  10. 移动端H5-iPhone安全距离适配