.NET Core中实现AOP编程
AOP全称Aspect Oriented Progarmming(面向切面编程),其实AOP对ASP.NET程序员来说一点都不神秘,你也许早就通过Filter来完成一些通用的功能,例如你使用Authorization Filter来拦截所有的用户请求,验证Http Header中是否有合法的token。或者使用Exception Filter来处理某种特定的异常。
你之所以可以拦截所有的用户请求,能够在期望的时机来执行某些通用的行为,是因为ASP.NET Core在框架级别预留了一些钩子,他允许你在特定的时机注入一些行为。对ASP.NET Core应用程序来说,这个时机就是HTTP请求在执行MVC Action的中间件时。
显然这个时机并不能满足你的所有求,比如你在Repository层有一个读取数据库的方法:
public void GetUser()
{
//Get user from db
}
你试图得到该方法执行的时间,首先想到的方式就是在整个方法外面包一层用来计算时间的代码:
public void GetUserWithTime()
{
var stopwatch = Stopwatch.StartNew();
try
{
//Get user from db
}
finally
{
stopwatch.Stop();
Trace.WriteLine("Total" + stopwatch.ElapsedMilliseconds + "ms");
}
}
如果仅仅是为了得到这一个方法的执行时间,这种方式可以满足你的需求。问题在于你有可能还想得到DeleteUser
或者UpdateUser
等方法的执行时间。修改每一个方法并添加计算时间的代码存在着明显的code smell。
一个比较优雅的做法是给需要计算时间的方法标记一个Attribute:
[Time]
public void GetUser()
{
//Get user from db
}
你把计算时间这个功能当做一个切面(Aspect)注入到了现有的逻辑中,这是一个AOP的典型应用。
在C#中使用AOP
C#中可以用来做AOP的开源类库有若干个,比较流行的:
这些类库之所以能够实现AOP是因为他们有动态修改IL代码的能力,这种能力又被称为IL weaving。
还有的类库把AOP和Dependency Injection结合在了一起,通过服务上注册一个拦截器(Interceptor)的方式做达到AOP的目的,例如:
本文将使用一个C#开源项目aspect-injector来描述AOP的几种常见的场景。
aspect-injector是一个非常轻量级的AOP类库,麻雀虽小,但是已经能够应对大部分AOP的应用场景:
- 支持.NET Core
- 支持对异步方法注入切面
- 能够把切面注入到方法、属性和事件上
- 支持Attribute的方式注入切面
注入计算执行时间的逻辑
在已有的方法上注入一段逻辑可以分为三种情况:
- 在方法执行前注入一段逻辑,例如注入统一的认证逻辑
- 在方法执行后注入一段逻辑,例如将结果写入日志
- 方法前后同时注入逻辑,例如计算时间,又或者给整个方法内容包裹一个事务
已知一个计算个数的方法如下:
public class SampleService
{
public int GetCount()
{
Thread.Sleep(3000);
return 10;
}
}
为了将计算时间的逻辑包裹在现有的方法上,我们需要在被注入逻辑的方法上标记InjectAttribute
:
public class SampleService
{
[Inject(typeof(TimeAspect))]
public int GetCount()
{
Thread.Sleep(3000);
return 10;
}
}
TimeAspect
就是我们将要注入的一个切面:
[Aspect(Aspect.Scope.Global)]
public class TimeAspect
{
[Advice(Advice.Type.Around, Advice.Target.Method)]
public object HandleMethod(
[Advice.Argument(Advice.Argument.Source.Name)] string name,
[Advice.Argument(Advice.Argument.Source.Arguments)] object[] arguments,
[Advice.Argument(Advice.Argument.Source.Target)]
Func<object[], object> method)
{
Console.WriteLine($"Executing method {name}");
var sw = Stopwatch.StartNew();
var result = method(arguments); //调用被注入切面的方法
sw.Stop();
Console.WriteLine($"method {name} in {sw.ElapsedMilliseconds} ms");
return result;
}
}
大部分代码是非常清晰的,我们只描述几个重要的概念:
标记了AdviceAttribute
的方法就是即将要注入到目标方法的切面逻辑,也就是说HandleMethod
描述了如何计算时间。
Advice.Type.Around
描述了同时在目标方法的前后都注入逻辑
方法参数Func<object[], object> method
其实就代表目标方法
注入认证逻辑
试想你有如果干个服务,每个服务在执行前都要做安全认证,显然安全认证的逻辑是可重用的,那我们就可以把认证的逻辑提取成一个切面(Aspect)。
[Inject(typeof(AuthorizationAspect))]
public class SampleService
{
public void MethodA(Guid userId)
{
// Do something
}
public void MethodB(Guid userId)
{
// Do something
}
}
AuthorizationAspect
就是安全认证的逻辑:
[Aspect(Aspect.Scope.Global)]
public class AuthorizationAspect
{
[Advice(Advice.Type.Before, Advice.Target.Method)]
public void CheckAccess(
[Advice.Argument(Advice.Argument.Source.Method)] MethodInfo method,
[Advice.Argument(Advice.Argument.Source.Arguments)] object[] arguments)
{
if (arguments.Length == 0 || !(arguments[0] is Guid))
{
throw new ArgumentException($"{nameof(AuthorizationAspect)} expects
every target method to have Guid as the first parameter");
}
var userId = (Guid)arguments[0];
if (!_securityService.HasPermission(userId, authorizationAttr.Permission))
{
throw new Exception($"User {userId} doesn't have
permission to execute method {method.Name}");
}
}
}
Advice.Type.Before
描述了该逻辑会在被修改的方法前执行
通过object[] arguments
得到了被修改方法的所有参数
AOP是面向对象编程中一种用来抽取公用逻辑,简化业务代码的方式,灵活使用AOP可以让你的业务逻辑代码不会过度臃肿,也是除了继承之外另一种可复用代码的方式。
.NET Core中实现AOP编程的更多相关文章
- (转).NET Core中实现AOP编程
原文地址:https://www.cnblogs.com/xiandnc/p/10088159.html AOP全称Aspect Oriented Progarmming(面向切面编程),其实AOP对 ...
- [翻译]在 .NET Core 中的并发编程
原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今天我们购买的每台电脑都有一个多核心的 C ...
- .NET Core 中的并发编程
今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点. 然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序 ...
- 聊Javascript中的AOP编程
Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...
- 聊聊Javascript中的AOP编程
Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...
- 在ASP.NET Core中使用AOP来简化缓存操作
前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...
- .NET下集中实现AOP编程的框架
一.Castle 使用这个框架呢,首先是需要安装NuGet包. 先建立一个控制台项目,然后在NuGet中搜索Castle.Windsor,不出意外的话应该能找到如下的包 然后安装,会自动的安装包Cas ...
- [译]如何在ASP.NET Core中实现面向切面编程(AOP)
原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...
- JavaEE开发之Spring中的依赖注入与AOP编程
上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...
随机推荐
- mstsc的事 随笔
当个备份吧, 记不得了,就翻一下自己的博客. MSTSC 设置, 平台:Windows 10 企业版 Windows 10 企业版,功能最全.
- LOJ.6435.[PKUSC2018]星际穿越(倍增)
LOJ BZOJ 参考这儿qwq. 首先询问都是求,向左走的最短路. \(f[i][j]\)表示从\(i\)走到\(j\)最少需要多少步.表示这样只会\(O(n^2\log n)\)的= =但是感觉能 ...
- Linux中安装MySQL
因为使用yum安装.安装过程需保证网络通畅 一.安装mysql 1.yum安装mysqlCentOS7默认数据库是mariadb,配置等用着不习惯,因此决定改成mysql,但是CentOS7的yum源 ...
- [jzoj]5478.【NOIP2017提高组正式赛】列队
Link https://jzoj.net/senior/#main/show/5478 Description Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校 ...
- webpack学习--安装
webpack需要在node环境运行,可以去node官网进行下载安装包:http://nodejs.cn/download/ 1.打开cmd命令窗口,运行node -v 2.全局安装webpack:n ...
- UVALive 2474 Balloons in a Box(枚举)
https://vjudge.net/contest/277824#problem/A 尤其是模拟题,三思而后敲!!! 纠错了好久,主要还是没有处理好:单点若还未放气球,其他气球可以膨胀越过它(即可以 ...
- Go语言基础之反射
Go语言基础之反射 本文介绍了Go语言反射的意义和基本使用. 变量的内在机制 Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息. 值信息:程序运行过程中可动态变化的. 反射介绍 反射是指 ...
- [LeetCode] Positions of Large Groups 大群组的位置
In a string S of lowercase letters, these letters form consecutive groups of the same character. For ...
- 数组的初始化&缩窄转换
1.初始化: 初始化就是在声明变量的同时给变量赋值,而不是声明后再赋值. 先声明,后赋值: int a; //先声明,由于没有初始化,所以当前a的值是变量a创建前,相应的内存单元中保留的值,是未知的 ...
- 循环结构for
教程:高能:语句结构都是由关键字开头,用冒号结束! 一:语句结构 for <variable> in <sequence>: <statements>else ...