zoukankan      html  css  js  c++  java
  • 常见的 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 了

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

  • 相关阅读:
    【转载】SAP_ECC6.0_EHP4或SAP_ECC6.0_EHP5_基于Windows_Server_2008R2_和SQL_server_2008下的安装
    使用delphi 开发多层应用(二十四)KbmMW 的消息方式和创建WIB节点
    使用delphi 开发多层应用(二十三)KbmMW 的WIB
    实现KbmMw web server 支持https
    KbmMW 服务器架构简介
    Devexpress VCL Build v2014 vol 14.1.1 beta发布
    使用delphi 开发多层应用(二十二)使用kbmMW 的认证管理器
    KbmMW 4.50.00 测试版发布
    Basic4android v3.80 beta 发布
    KbmMW 认证管理器说明(转载)
  • 原文地址:https://www.cnblogs.com/fs7744/p/14137961.html
Copyright © 2011-2022 走看看