啥是Aop呢,面向切面,啥是面向切面呢,就不太好一句话说明白了。
但是,其实不论在生活中还是工作研发中,我们都能找到它。
在研发中,它其实就是个过滤器,每个请求来了。它都要检查一下。
在生活中,它就是父母的谆谆教导,回家就得问你吃了么。.
在工作中,它就是打卡机,就是刷卡机,人脸识别,总得检查你一下子。
官方解释为:面向切面编程,主要是对权限检查,日志记录,性能统计,安全控制,事务处理,异常处理,从权限检查来看,就已经很明显的了解它是干啥子的了。当然,实际用的时候,不仅仅可以检查权限。还可以搞别的。
AOP主要的作用:从ASP.NET MVC Filter(拦截器,过滤器)的作用我们知道,拦截,一般拦截在执行方法体的前面,或者,执行方法体的返回,主要是这两部分。所以,就是从方法体执行前逻辑处理,以及在方法体执行后逻辑处理。
.Net 动态代理
想实现AOP,就得通过代理层来实现,那么,我们就通过动态代理来实现。
不懂动态代理的,可以参考设计模式之代理(中介)模式即可。其实就像生活中租房子要找中介一样。
.NET 下动态代理技术方案
.Net框架自带
-
1. DispatchProxy
-
2. Realproxy
-
3. DynamicObject
开源的框架有
-
1. ImpromptuInterface
-
2. PostSharp1.5
-
3. Castle.DynamicProxy
实现代理的方式有两种,一种是静态织入,一种是动态织入。
静态织入的话,相当于是在不影响业务的情况下,又对框架做了一层处理(PostSharp1.5)影响在DLL中。
而动态织入就是在应用运行的过程中动态插入到里面,影响在运行时。
大部分都是采用动态织入实现的。
我这边要实现一个通用性屏蔽.NetFW和.Net Coer之间的差异,所以,不能直接用框架自带的,也不想用开源框架里的,感觉它们写的有点复杂。
所以,得使用反射来实现这样的一个功能。
具体作用,我是准备用在自己要写的RPC中,所以,这个主要针对接口部分做的动态代理
具体原理如下:
用RPC 的时候,需要公共的部分,这部分就是接口。
啥是RPC
RPC 全称是 Remote Procedure Call ,即远程过程调用,其对应的是我们的本地调用。远程其实指的就是需要网络通信,可以理解为调用远程机器上的方法。
那跟HTTP,WebApi,一样,直接调用不就成了。不是的,RPC,要实现的是,调用服务端的方法,就像服务端调用自己的方法一样,有返回类型,有参数,参数类型。
注:关于调试
只有 fw 框架才会能保存dll查看,如果用到.NetCore项目只能直接运行,所以,想要调试,还是要在fw框架下调试
生成的dll 可以用 反编译软件 dnSpy 进行获取。
也可以根据dnSpy这样的反编译软件获取IL代码进行编码
客户端会共享这个接口,如下。
public interface IConsole
{
void Say();
}
客户端调用的方式会像这样,就实现了客户端与服务端的RPC调用
var console = Activator.CreateInstance(dynamicType) as IConsole;
console.Say();
动态代理简单实现
public interface IConsole
{
void Say();
}
class Program
{
/// <summary>
/// .net fw项目,可以实现程序集的运行和保存,但是,.Net Core的就只能运行了
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
// 在看下面的代码之前,先明白这个依赖关系,即:
// 方法->类型->模块->程序集
//定义程序集的名称
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
// 创建一个程序集构建器
// Framework 也可以这样:AppDomain.CurrentDomain.DefineDynamicAssembly
AssemblyBuilder ab =
AssemblyBuilder.DefineDynamicAssembly(
aName,
AssemblyBuilderAccess.RunAndSave);
// 使用程序集构建器创建一个模块构建器
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
// 使用模块构建器创建一个类型构建器
TypeBuilder tb = mb.DefineType("DynamicConsole");
// 使类型实现IConsole接口
tb.AddInterfaceImplementation(typeof(IConsole));
var attrs = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig | MethodAttributes.Final;
// 使用类型构建器创建一个方法构建器
MethodBuilder methodBuilder = tb.DefineMethod("Say", attrs, typeof(void), Type.EmptyTypes);
// 通过方法构建器获取一个MSIL生成器
var IL = methodBuilder.GetILGenerator();
// 开始编写方法的执行逻辑
// 将一个字符串压入栈顶
IL.Emit(OpCodes.Ldstr, "I'm here.");
// 调用Console.Writeline函数
IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));
// 退出函数
IL.Emit(OpCodes.Ret);
//方法结束
// 从类型构建器中创建出类型
var dynamicType = tb.CreateType();
//ab.Save(aName.Name + ".dll");
// 通过反射创建出动态类型的实例
var console = Activator.CreateInstance(dynamicType) as IConsole;
console.Say();
ab.Save("DynamicAssemblyExample.dll");
Console.WriteLine("不错,完成了任务!");
Console.ReadLine();
}
}
运行后,结果如下:

反编译如下: 自己继承接口,并实现这个接口的类,然后,反射出来。

反编译的IL代码如下

可以看到,跟我们写的emit代码是一致的。
结束
至此,核心原理已经完了。
接下来就是实际过程中的代理过程
会让一个代理对象成为这个接口类的字段。然后在每个方法的执行过程中都会调用这个对象执行,并返回相应的结果。
这个对象执行的过程就是调用方法到远程服务器并返回结果的过程。
下一节,会实现一个简单的RPC 项目。
代码地址
https://github.com/kesshei/DynamicProxyDemo.git
https://gitee.com/kesshei/DynamicProxyDemo.git