AOP从静态代理到动态代理 Emit实现
【前言】
AOP框架呢,一般通过静态代理和动态代理两种实现方式。
何为静态代理?
动态代理,又成为运行时代理。在程序运行的过程中,调用了生成代理类的代码,将自动生成业务类的代理类。不需要我们手共编写,极高的提高了工作效率和调整了程序员的心态。
原理不必多说,就是动态生成静态代理的代码。我们要做的,就是选用一种生成代码的方式去生成。
今天我分享一个简单的AOP框架,代码使用Emit生成。当然,Emit 代码的写法不是今天要讲的主要内容,需要提前去学习。
先说效果:
定义一个Action特性类 ActionAttribute 继承自 ActionBaseAttribute,里面在Before和After方法中输出两条日志;
定义一个Action特性类 InterceptorAttribute 继承自 InterceptorBaseAttribute,里面捕获了方法调用异常,以及执行前后分别输出日志;
然后定义一个业务类 BusinessClass 实现了 IBusinessClass 接口,定义了各种类型的方法
多余的方法不贴图了。
我们把上面定义的方法调用切面标签放在业务类上,表示该类下所有的方法都执行异常过滤;
我们把Action特性放在Test方法上,表明要在 Test() 方法的 Before 和 After 调用时记录日志;
我们定义测试类:
调用一下试试:
可见,全类方法标签 Interceptor 在 Test 和 GetInt 方法调用前后都打出了对应的日志;
Action方法标签只在 Test 方法上做了标记,那么Test 方法 Before 和 After 执行时打出了日志;
【实现过程】
解释:如果该方法存在Action标签,那么加载 action 标签实例化对象,加载参数,执行Before方法;如果该方法存在Interceptor标签,那么使用类字段this._interceptor调用该标签的Invoke方法。
解释:如果面的Interceptor特性标签不存在,那么会加载当前扫描的方法对应的参数,直接调用方法;如果Action标签存在,则将刚才调用的结果包装成object对象传递到After方法中。
这里如果目标参数是object类型,而实际参数是直接调用返回的明确的值类型,需要进行装箱操作,否则运行时报调用内存错误异常。
解释:如果返回值是void类型,则直接结束并返回结果;如果返回值是值类型,则需要手动拆箱操作,如果是引用类型,那么需要类型转换操作。
IL实现的细节,这里不做重点讨论。
【系统测试】
结论:对于上述穷举的类型,各种标签使用方式皆成功打出了日志;
2.继承方式,Api测试(各种标签使用方式对应的不同类型的方法调用):
结论:直接调用三个方法百万次调用耗时 58ms
4.使用实现接口方式三个方法百万次调用结果
结论:结果见上图,需要注意是三个方法百万次调用,也就是300w次的方法调用
事实证明,IL Emit的实现方式性能还是很高的。
综合分析:
通过自己实现一个AOP的动态注入框架,对Emit有了更加深入的了解,最重要的是,对CLR IL代码的执行过程有了一定的认知,受益匪浅。
该方法在使用的过程中也发现了问题,比如有ref和out类型的参数时,会出现问题,需要后续继续改进
本文的源代码已托管在GitHub上,又需要可以自行拿取(顺手Star哦~):https://github.com/sevenTiny/CodeArts (可以访问下面链接获取.Net Standard 跨平台版本代码)
最新SevenTiny.Bantina.Aop组件代码(跨平台):https://github.com/sevenTiny/SevenTiny.Bantina
该代码的位置在 CodeArts.CSharp 分区下
VS打开后,可以在 EmitDynamicProxy 分区下找到;本博客所有的测试项目都在项目中可以找到。
再次放上源代码地址,供一起学习的朋友参考,希望能帮助到你:https://github.com/sevenTiny/CodeArts (可以访问下面链接获取.Net Standard 跨平台版本代码)
最新SevenTiny.Bantina.Aop组件代码(跨平台):https://github.com/sevenTiny/SevenTiny.Bantina
出处:https://www.cnblogs.com/7tiny/p/9657451.html