简单记录一下对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# 实现 AOP 面向切面编程的更多相关文章

  1. Method Swizzling和AOP(面向切面编程)实践

    Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...

  2. [转] AOP面向切面编程

    AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  3. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

  4. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  5. 论AOP面向切面编程思想

    原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...

  6. 学习笔记: AOP面向切面编程和C#多种实现

    AOP:面向切面编程   编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统      类--砖头     系统--房子      类--细胞     系统--人    ...

  7. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...

  8. AOP 面向切面编程, Attribute在项目中的应用

    一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...

  9. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...

  10. C# AOP 面向切面编程之 调用拦截

    有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...

随机推荐

  1. Linux NAS存储、文件共享

    Linux NAS存储之CIFS CIFS是Windows和Unix系统之间共享文件的一种协议,客户端通常是Windwos等.支持多节点同时挂载以及并发写入 1.服务器端操作(存储端) 1.1.服务器 ...

  2. 【YashanDB知识库】Oracle pipelined函数在YashanDB中的改写

    本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802940.html?templateId=1718516 [问题分类]功能使用 ...

  3. 关于Java的UUID

    UUID或者UNID或者UID,是一个统一唯一标识,可以用来标记文档.数据或其它需要唯一标识的东西.Java 5.0内置UUID的实现,见java.util.UUID. 下面代码是找到的2种实现方式, ...

  4. 问题:Opencv(3.1.0/3.4)找不到 /opencv2/gpu/gpu.hpp 问题

    解决:Opencv(3.1.0/3.4/4.1.0)找不到 /opencv2/gpu/gpu.hpp 问题 环境: 系统:Win10 环境:Opencv3.4.0/4.1.0 显卡:1080ti 问题 ...

  5. [转]win10 vue-cli 下载成功,显示 vue不是内部或外部命令,也不是可运行的程序

    vue不是内部或外部命令,也不是可运行的程序 1.安装vue-cli npm install -g @vue/cli 2.查看是否安装成功 vue -V 经过上述步骤,一般在CMD窗口输入" ...

  6. Object.freeze冻结属性和v-if结合requestAnimationFrame分帧渲染解决白屏

    计算100W条数据的长度造成2s延迟 <template> <div> <h1>数据总长度{{ arrList.length }}</h1> </ ...

  7. conda创建label标注环境

    conda create -n label python=3.6 conda activate label pip install labeimg -i https://pypi.tuna.tsing ...

  8. 展锐SE8451E 开启硬件流控

    Dear Customer:  如电话沟通,若将uart0配置成3M波特率,需进行如下更改:  1.时钟源更改为96M/sprdroid10_trunk_19c_rls1/bsp/kernel/ker ...

  9. mac zshrc环境变量配置

      配置mac zshrc的环境变量时犯了一个错误,我配置了多个export PATH, 结果只有一个PATH生效了,所以配置多个PATH是错误的: zshrc中环境变量配置如下: export AN ...

  10. SQL统计数据之总结

    一.查询SQL SELECT t1.规则编号 AS 编码, t1.规则描述 AS 名称, SUM( CASE WHEN t3.DATA_SOURCES = '00' THEN 1 ELSE 0 END ...