【代码设计】C# 实现 AOP 面向切面编程
简单记录一下对AOP的认识,正文为3个部分
一、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 面向切面编程的更多相关文章
- Method Swizzling和AOP(面向切面编程)实践
Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...
- [转] AOP面向切面编程
AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...
- 【原创】Android AOP面向切面编程AspectJ
一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...
- 论AOP面向切面编程思想
原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...
- 学习笔记: AOP面向切面编程和C#多种实现
AOP:面向切面编程 编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统 类--砖头 系统--房子 类--细胞 系统--人 ...
- Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...
- AOP 面向切面编程, Attribute在项目中的应用
一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...
- Javascript aop(面向切面编程)之around(环绕)
Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...
- C# AOP 面向切面编程之 调用拦截
有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...
随机推荐
- 简单软件架构的一些好处zz
简单软件架构的一些好处_大数据_Dan Luu_InfoQ精选文章 Wave 是一家价值 17 亿美元的公司,拥有 70 名工程师,该公司的产品是一款加减数字的 CRUD 应用程序.为了与此保持一致, ...
- Shiro配置跳过权限验证
需求 因为在开发环境,测试环境,有时候需要跳过shiro的权限验证.所以想写个简单的配置跳过shiro的权限验证.跳过权限验证的原理就是重写**@RequiresPermissions**的实现,然后 ...
- Qt编写地图综合应用9-行政区划
一.前言 行政区划在地图应用中非常有用,行政区划是行政区域划分的简称,是国家为了进行分级管理而实行的区域划分,百度地图提供的内置的函数类支持传入行政区划的名称来获取对应的边界点集合,然后根据该集合来绘 ...
- Qt音视频开发16-mpv通用接口
一.前言 前面几篇文章,依次讲了解码播放.录像存储.读取和控制.事件订阅等,其实这些功能的实现都离不开封装的通用的接口,最开始本人去调用一些设置的时候,发现多参数的不好实现,原来需要用mpv_node ...
- Dotnetbar自定义DateTimeInput(时间)控件的显示格式,使其同时显示日期和时间
DateTimeInput控件已有的几种格式可以在Format属性中选择: 但这几种格式仍无法满足我的要求怎么办? 例如想将显示格式定为类似这样的格式:2010-06-11 20:02:52,两步搞定 ...
- 使用Matlab进行图像分类
Classify images Instructions are in the task pane to the left. Complete and submit each task one at ...
- JavaScript 数组展平方法: flat() 和 flatMap()
从 ES2019 中开始引入了一种扁平化数组的新方法,可以展平任何深度的数组. flat flat() 方法创建一个新数组,其中所有子数组元素以递归方式连接到特定深度. 语法:array.flat(d ...
- Appium_ios自动化问题汇总
1.使用Xcode遇到的问题 xcode-select: error: tool 'instruments' requires Xcode, but active developer director ...
- 一站式解决方案 :OFD电子证照生成
前言 证照的电子化是一个趋势:可以预计,未来几年内,绝大部分证照都会电子化.电子证照的种类越来越多,应用场景也复杂多样:这就给电子证照规范的制定.电子证照的生成提出了更高的要求.电子证照采用的格式有两 ...
- 题解:AT_abc385_f [ABC385F] Visible Buildings
小学二年级就会的基本一次函数知识. 抽象一下题意: 求 \(n\) 个点 \((X_i, H_i)\) 中任意两个点组成的直线的截距的最大值(不小于 \(0\),\(X_i\) 递增). 先说结论:先 ...