• 常见的 emit 实现 AOP demo


    0. 前言

    上接:思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式

    作为第四篇,我们回顾一下 csharp 里面比较常见动态编织实现方式emit

    内容安排如下:

    • emit aop demo
    • Norns.Urd

    1. emit aop demo

    1.1 emit 介绍

    emit 是类似 java 中ASM地位的一个底层功能实现,

    不过不是转化java字节码,而是生成dotnet 的 IL代码,

    生成的IL代码将由内置的JIT编译器直接编译到内存中。

    官方的介绍文档

    emit 对大家来说都是很熟悉的api了,动态做什么事基本都会想到它。

    我们是可以使用emit 做到上篇java 的 cglib 一模一样的动态编织的AOP效果,所以语言真的只是工具,怎么玩取决于玩工具的人,demo 如下。

    1.2 demo

    代码

    1.2.1 ProxyGenerator 简单实现

    public abstract class MethodInterceptor
    {
        public abstract object Invoke(object instance, MethodInfo methodInfo, object[] parameters, object returnValue);
    }
    
    
    public static class ProxyGenerator
    {
        private static ModuleBuilder moduleBuilder;
        private static MethodInfo getMethodMethod = typeof(MethodBase).GetMethod("GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle) });
        private static MethodInfo invoke = typeof(MethodInterceptor).GetMethod("Invoke");
        static ProxyGenerator()
        {
            var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("EmitAopDemoTest"), AssemblyBuilderAccess.RunAndCollect);
            moduleBuilder = asmBuilder.DefineDynamicModule("Proxy");
        }
        public static T Generate<T>(Type methodInterceptorType)
        {
            var proxyType = GenerateProxyType(typeof(T), methodInterceptorType);
            return (T)Activator.CreateInstance(proxyType);
        }
        public static Type GenerateProxyType(Type type, Type methodInterceptorType)
        {
            var typeBuilder = moduleBuilder.DefineType($"{type.Name}Proxy", TypeAttributes.Class | TypeAttributes.Public, type);
            foreach (var m in type.GetTypeInfo().DeclaredMethods)
            {
                var ps = m.GetParameters().Select(i => i.ParameterType).ToArray();
                var newM = typeBuilder.DefineMethod(m.Name, MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, m.CallingConvention, m.ReturnType, ps);
                CreateProxyMethod(methodInterceptorType, m, ps, newM);
                typeBuilder.DefineMethodOverride(newM, m);
            }
            return typeBuilder.CreateType();
        }
        private static void CreateProxyMethod(Type methodInterceptorType, MethodInfo m, Type[] ps, MethodBuilder newM)
        {
            var il = newM.GetILGenerator();
            var argsLocal = il.DeclareLocal(typeof(object[]));
            var returnLocal = il.DeclareLocal(typeof(object));
            // 初始化参数集合
            il.Emit(OpCodes.Ldc_I4, ps.Length);
            il.Emit(OpCodes.Newarr, typeof(object));
            for (var i = 0; i < ps.Length; i++)
            {
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Ldc_I4, i);
                il.Emit(OpCodes.Ldarg, i + 1);
                il.Emit(OpCodes.Box, ps[i]);
                il.Emit(OpCodes.Stelem_Ref);
            }
            il.Emit(OpCodes.Stloc, argsLocal);
            // 调用被代理方法
            il.Emit(OpCodes.Ldarg, 0); // load this
            for (var i = 0; i < ps.Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i + 1);
            }
            il.Emit(OpCodes.Call, m);
            il.Emit(OpCodes.Box, newM.ReturnType);
            il.Emit(OpCodes.Stloc, returnLocal);
            //调用方法后拦截器
            il.Emit(OpCodes.Newobj, methodInterceptorType.GetConstructors().First());
            il.Emit(OpCodes.Ldarg, 0); // load this
            //加载方法信息参数
            il.Emit(OpCodes.Ldtoken, m);
            il.Emit(OpCodes.Call, getMethodMethod);
            il.Emit(OpCodes.Castclass, typeof(MethodInfo));
            il.Emit(OpCodes.Ldloc, argsLocal);
            il.Emit(OpCodes.Ldloc, returnLocal);
            il.Emit(OpCodes.Callvirt, invoke);
            il.Emit(OpCodes.Stloc, returnLocal);
            // return
            il.Emit(OpCodes.Ldloc, returnLocal);
            il.Emit(OpCodes.Unbox_Any, newM.ReturnType);
            il.Emit(OpCodes.Ret);
        }
    }
    

    1.2.2 Test

     internal class Program
     {
         private static void Main(string[] args)
         {
             RealClass proxy = ProxyGenerator.Generate<RealClass>(typeof(AddOneInterceptor));
             var i = 5;
             var j = 10;
             Console.WriteLine($"{i} + {j} = {(i + j)}, but proxy is {proxy.Add(i, j)}");
         }
     }
    

    结果:

    5 + 10 = 15, but proxy is 16
    

    2. Norns.Urd

    至此,

    本系列已经介绍完了所有的aop实现方式,

    以csharp 平台重点举例介绍了AOP 静态编织和动态编织 的方法。

    并以 java cglib 表达了思想无编程语言边界。

    最后呢,介绍一下自己正在做的项目 Norns.Urd
    Github: https://github.com/fs7744/Norns.Urd

    Norns.Urd 是一个基于emit实现动态代理的轻量级AOP框架.

    版本基于 netstandard2.0. 所以哪些.net 版本能用你懂的。
    完成这个框架的目的主要出自于个人以下意愿:

    • 静态AOP和动态AOP都实现一次
    • 如果不实现DI,怎么将AOP框架实现与其他现有DI框架集成
    • 一个AOP 如何将 sync 和 async 方法同时兼容且如何将实现选择权完全交予用户
      希望该库能对大家有些小小的作用, 开源不易,大家可以给个star 就非常nice 了

    希望看过大家做码农做的开心。

  • 相关阅读:
    特效优化
    Jsp
    JRebel 热部署
    mysql
    行为树
    Medium | LeetCode 139. 单词拆分 | 动态规划
    Medium | LeetCode 31. 下一个排列
    Easy | LeetCode 27. 移除元素 | 快慢指针
    Medium | LeetCode 437. 路径总和 III | 树 + 回溯 + 前缀和
    Medium | LeetCode 337. 打家劫舍 III | 树后序遍历 + 动态规划
  • 原文地址:https://www.cnblogs.com/fs7744/p/14137961.html
Copyright © 2020-2023  润新知