一.AOP概述:
AOP(Aspect Oriented Programming),即面向切面编程。采用“横切”的思想,将软件系统的系统功能和业务功能分开。
系统功能主要有:
1.缓存模块:用于缓存数据,与系统业务功能关系不大,用于提升系统性能。当数据请求过来时,是否要从缓存读取,当缓存没有数据时,
是否要将获取到的数据放入缓存,以便下次数据请求时从缓存读取。比较常用的缓存工具有monogodb,redis。
2.验证模块:判断当前用户是否有获取该条数据 或者执行 该操作的权限。
3.日志模块:收集当前用户的操作痕迹,或者记录系统出现的问题,后期可进行大数据行为分析。
业务功能:系统处理用户请求的正常逻辑功能。
http请求过来后,会先经过系统功能的横切拦截,才会到业务功能处理请求。AOP具体过程如下图所示:
二、AOP实现:
国内大佬基于.NETStand 实现了一个AOP的框架,其源码地址为 https://github.com/dotnetcore/AspectCore-Framework。
要使用该框架,可通过Nuget 将 AspectCore.Extensions.DependencyInjection 引用到项目中,可实现日志拦截和缓存拦截,该框架是基于IOC容器的。
这里,我用控制台程序来演示:首先,先nuget 引用 AspectCore.Extensions.DependencyInjection。
1.程序入口:
class Program { static void Main(string[] args) { //创建用于注册服务的IOC容器 ServiceCollection ServiceCollection services = new ServiceCollection(); //在容器中 注册服务 services.ConfigureDynamicProxy(); services.AddScoped<IMySql, MySql>(); //创建ServiceProvider,用于获取服务。 var provider = services.BuildDynamicProxyProvider(); //获取服务。 var mysql = provider.GetService<IMySql>(); //执行服务。 var msg=mysql.GetById(10); //第一次去数据,从业务逻辑取数据 var value = mysql.GetData("hehe"); Console.WriteLine(value); //第二次去数据,从缓存取 value = mysql.GetData("hehe"); Console.WriteLine(value); Console.ReadKey(); Console.WriteLine("Hello World!"); } }
2.系统功能 切面部分(AOP):
日志切面:
/// <summary> /// 日志模块:(日志切面,日志AOP) /// </summary> public class MyLogInterceptorAttribute : AbstractInterceptorAttribute { /// <summary> /// /// </summary> /// <param name="context">委托的参数,这里传递的是 GetById 方法</param> /// <param name="next">委托,委托的动作要看具体传进来的方法的动作是什么</param> /// <returns></returns> public override Task Invoke(AspectContext context, AspectDelegate next) { Console.WriteLine("开始记录日志......."); //var aa = context; //var bb = next; var task = next(context);//从这里开始执行具体的方法。这里执行的是 GetById; Console.WriteLine("结束记录日志......."); return task; } }
缓存切面:
public class MyCacheInterceptorAttribute : AbstractInterceptorAttribute { //用于模拟缓存 private Dictionary<string, string> CacheDic = new Dictionary<string, string>(); /// <summary> /// /// </summary> /// <param name="context">委托的参数,这里传递的是 GetById 方法</param> /// <param name="next">委托,委托的动作,要看具体传进来的方法的动作是什么</param> /// <returns></returns> public override Task Invoke(AspectContext context, AspectDelegate next) { //获取 传递过来的AspectContext,即传进来的方法 的参数。 var cacheKey = string.Join(",", context.Parameters); //从缓存去数据。如果缓存有,则返回 if (CacheDic.ContainsKey(cacheKey)) { context.ReturnValue = CacheDic[cacheKey].ToString(); return Task.CompletedTask; } //缓存没有,从业务逻辑取数据,去到数据后,将数据放入缓存 var task = next(context); var cacheValue = context.ReturnValue.ToString(); CacheDic.Add(cacheKey, "From cache:"+cacheValue); return task; } }
3.业务功能部分:
public interface IMySql { string GetById(int id); string GetData(string key); } public class MySql : IMySql { [MyLogInterceptor] //日志切面 public string GetById(int id) { var msg = $"已经获取到ID为{id}的数据"; Console.WriteLine(id); return msg; } [MyCacheInterceptor] //缓存切面 public string GetData(string key) { return "获取数据"; } }