.net 使用IL生成代理类实现AOP对比Java Spring Boot的AOP

首先,我们需要定义一个接口,代表我们要代理的目标对象的功能:

// 日志记录器接口
public interface ILogger
{
    /// 
    /// 记录日志
    /// 
    /// 日志消息
    void Log(string message);
}

// 日志记录器实现类
public class Logger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($"Logging: {message}");
    }
}

然后,我们创建一个代理类,实现该接口,并在目标方法的执行前后注入额外的逻辑:

// 切面接口
public interface IInterceptor
{
    /// 
    /// 在方法执行前执行的逻辑
    /// 
    /// 目标类型
    /// 方法名称
    void BeforeMethod(Type targetType, string methodName);

    /// 
    /// 在方法执行后执行的逻辑
    /// 
    /// 目标类型
    /// 方法名称
    void AfterMethod(Type targetType, string methodName);
}

// 日志记录切面类
public class LoggingAspect : IInterceptor
{
    public void BeforeMethod(Type targetType, string methodName)
    {
        Console.WriteLine($"Before {targetType.Name}.{methodName}");
    }

    public void AfterMethod(Type targetType, string methodName)
    {
        Console.WriteLine($"After {targetType.Name}.{methodName}");
    }
}

接下来,我们通过使用IL生成代理类型的字节码,动态创建代理对象:

// 代理生成器
public static class ProxyGenerator
{
    /// 
    /// 创建代理对象
    /// 
    /// 目标接口类型
    /// 目标对象实例
    /// 切面对象实例
    /// 代理对象
    public static TInterface CreateProxy(TInterface target, IInterceptor interceptor)
        where TInterface : class
    {
        Type targetType = typeof(TInterface);
        TypeBuilder typeBuilder = CreateTypeBuilder(targetType);
        ImplementInterface(typeBuilder, targetType);
        ImplementMethods(typeBuilder, targetType, interceptor);
        Type proxyType = typeBuilder.CreateType();
        return Activator.CreateInstance(proxyType, target, interceptor) as TInterface;
    }

    private static TypeBuilder CreateTypeBuilder(Type targetType)
    {
        string typeName = targetType.Name + "Proxy";
        AssemblyName assemblyName = new AssemblyName(typeName);
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(typeName + "Module");
        TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
        typeBuilder.AddInterfaceImplementation(targetType);
        return typeBuilder;
    }

    private static void ImplementInterface(TypeBuilder typeBuilder, Type targetType)
    {
        foreach (MethodInfo method in targetType.GetMethods())
        {
            Type[] parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
            MethodBuilder methodBuilder = typeBuilder.DefineMethod(
                method.Name,
                MethodAttributes.Public | MethodAttributes.Virtual,
                method.ReturnType,
                parameterTypes
            );
            typeBuilder.DefineMethodOverride(methodBuilder, method);
        }
    }

    private static void ImplementMethods(TypeBuilder typeBuilder, Type targetType, IInterceptor interceptor)
    {
        foreach (MethodInfo method in targetType.GetMethods())
        {
            MethodBuilder methodBuilder = typeBuilder.GetMethod(method.Name).GetBaseDefinition() as MethodBuilder;
            if (methodBuilder != null)
            {
                ILGenerator ilGenerator = methodBuilder.GetILGenerator();

                // 调用切面方法
                ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈
                ilGenerator.Emit(OpCodes.Ldfld, typeBuilder.GetField("interceptor")); // 加载interceptor到堆栈
                ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈
                ilGenerator.Emit(OpCodes.Ldtoken, targetType); // 加载targetType到堆栈
                ilGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); // 调用GetTypeFromHandle方法
                ilGenerator.Emit(OpCodes.Ldstr, method.Name); // 加载方法名到堆栈
                ilGenerator.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("BeforeMethod")); // 调用BeforeMethod方法
                ilGenerator.Emit(OpCodes.Pop); // 丢弃返回值

                // 调用目标方法
                ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈
                for (int i = 1; i <= method.GetParameters().Length; i++)
                {
                    ilGenerator.Emit(OpCodes.Ldarg_S, i); // 加载方法参数到堆栈
                }
                ilGenerator.Emit(OpCodes.Callvirt, targetType.GetMethod(method.Name)); // 调用目标方法

                // 调用切面方法
                ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈
                ilGenerator.Emit(OpCodes.Ldfld, typeBuilder.GetField("interceptor")); // 加载interceptor到堆栈
                ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈
                ilGenerator.Emit(OpCodes.Ldtoken, targetType); // 加载targetType到堆栈
                ilGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); // 调用GetTypeFromHandle方法
                ilGenerator.Emit(OpCodes.Ldstr, method.Name); // 加载方法名到堆栈
                ilGenerator.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("AfterMethod")); // 调用AfterMethod方法
                ilGenerator.Emit(OpCodes.Pop); // 丢弃返回值
            }
        }
    }
}

最后,我们可以使用以下代码来测试动态代理的功能:

class Program
{
    static void Main(string[] args)
    {
        // 创建目标对象
        ILogger logger = new Logger();

        // 创建切面对象
        IInterceptor interceptor = new LoggingAspect();

        // 创建代理对象
        ILogger proxy = ProxyGenerator.CreateProxy(logger, interceptor);

        // 调用代理对象的方法
        proxy.Log("Hello, World!");

        Console.ReadLine();
    }
}

对比一下Java Spring Boot 的AOP

  1. 动态代理实现方式:在Java Spring Boot中,基于代理的AOP主要使用JDK动态代理和CGLIB代理来实现。而在C#中,使用IL生成器(ILGenerator)直接操作IL指令来生成和修改类型的字节码,实现动态代理。这种方式相对于代理类的生成更加底层,需要对CLR(Common Language Runtime)和IL指令有一定的了解。

  2. IL的语法和特性:IL是.NET平台的中间语言,类似于汇编语言,但具有一些.NET特定的语法和特性。IL指令用于描述类型、方法、属性等的结构和操作,需要了解这些指令的使用规则和约束。相比之下,Java字节码(Java bytecode)是Java虚拟机(JVM)上的中间语言,其语法和特性与IL有所不同。

  3. 第三方库和框架:在Java生态系统中,有许多第三方库和框架(如AspectJ、Spring AOP)提供了高级别的API和工具,使AOP的使用更加方便。而在C#中,虽然也有一些库(如Castle DynamicProxy、Unity Interception)可以辅助实现AOP,但相对于Java生态系统来说,可选择的工具和框架较少

    综上所述,C#使用IL实现AOP与Java Spring Boot的AOP在实现方式、编程语言和生态系统等方面存在一些不同。C#开发者需要直接操作IL指令来生成和修改类型的字节码,需要对CLR和IL指令有一定的了解。而Java Spring Boot的AOP则基于代理实现,使用注解和切点表达式等高级别的API来配置和管理AOP。

你可能感兴趣的:(.net)