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

Ioc