当前位置: 首页 > article >正文

Castle.DynamicProxy的NET Core和Framework的AOP实施

Castle.DynamicProxy

    • 拦截类,虚成员
    • 拦截接口
      • 不指定实现进行代理CreateInterfaceProxyWithoutTarget
      • 指定实现进行代理CreateInterfaceProxyWithTarget
      • CreateInterfaceProxyWithTargetInterface
    • 异步方法代理
      • 代理类
      • 代理接口

![[Pasted image 20241112171900.png]]

远程处理中的代理要求您从MarshalByRefObject派生类,但如果您无法更改类的基类,这是不切实际的。DynamicProxy我们可以在不更改基类的情况下代理我们的类,尽管我们需要类成员是virtual的才能使用此代码。

借鉴

拦截类,虚成员

Castle DynamicProxy 提供了两种创建类代理的方法:CreateClassProxyWithTargetCreateClassProxy。以下是这两种方法的解释和对比:

  1. CreateClassProxyWithTarget

    • CreateClassProxyWithTarget 方法用于创建一个代理对象,该对象包装了一个已经存在的目标对象实例。这意味着你可以为目标对象提供一个实例,并且所有的方法调用(包括非虚方法)都会通过拦截器,然后转发到目标对象。
    • 这种方式下,你可以拦截并修改目标对象的行为,包括非虚方法和属性的访问。
    • 它允许你传递一个目标对象实例和一组拦截器,代理对象将使用这些拦截器来拦截对目标对象的调用。
    • 例如,如果你有一个类 ProductRepository 和一个拦截器 LoggerInterceptor,你可以这样创建代理:
      ProxyGenerator generator = new ProxyGenerator();
      IInterceptor loggerIntercept = new LoggerInterceptor();
      ProductRepository target = new ProductRepository();
      ProductRepository proxy = generator.CreateClassProxyWithTarget(target, loggerIntercept);
      
    • 这种方法适用于需要代理非虚方法或字段的场景,且要求目标类是可继承的。
  2. CreateClassProxy

    • CreateClassProxy 方法用于创建一个代理对象,该对象不需要一个实际的目标对象实例。它在运行时动态生成一个代理类,这个类继承自指定的类,并重写其中的虚方法。
    • 这种方式下,只有目标类中的虚方法可以被拦截和修改。
    • 你只需要传递一个拦截器实例,代理对象将拦截对目标类的所有虚方法的调用。
    • 例如,如果你有一个类 ProductRepository 和一个拦截器 LoggerInterceptor,你可以这样创建代理:
      ProxyGenerator generator = new ProxyGenerator();
      IInterceptor loggerIntercept = new LoggerInterceptor();
      ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept);
      
    • 这种方法适用于目标类中的方法是虚方法,并且你不需要代理非虚方法的场景。

总结来说,CreateClassProxyWithTarget 允许你代理一个已经存在的对象实例,包括非虚方法,而 CreateClassProxy 只能代理目标类中的虚方法。选择哪种方法取决于你的具体需求,是否需要代理非虚方法,以及是否已经有一个目标对象实例。

我们将创建一个拦截器,顾名思义,它将用于拦截动态代理对我们对象的调用,在这种情况下,我们将把调用的方法/属性记录到Console中。

public class Interceptor : IInterceptor
{
   public void Intercept(IInvocation invocation)
   {
      Console.WriteLine($"Before target call {invocation.Method.Name}" );
      try
      {
         invocation.Proceed();
      }
      catch (Exception e)
      {
         Console.WriteLine($"Target exception {ex.Message}");
         throw;
      }
      finally
      {
         Console.WriteLine($"After target call {invocation.Method.Name}");
      }
   }
}

使用者

public class MyClass
{
   public virtual bool Flag { get; set; }
 
   public virtual void Execute()
   {
      Console.WriteLine("Execute method called");
   }
}

创建代理

var proxy = new ProxyGenerator()
   .CreateClassProxy<MyClass>(
       new Interceptor());
proxy.Flag = true;
proxy.Execute();

输出

Before target call set_Flag
After target call set_Flag
Before target call Execute
Execute method called
After target call Execute

拦截接口

在这种情况下,代理基于接口,只需调用“目标”对象属性/方法。因此,这种调用转发意味着目标对象不需要将方法/属性标记为虚拟。

CreateInterfaceProxyWithoutTargetCreateInterfaceProxyWithTarget 是 Castle DynamicProxy 库中用于创建代理对象的两个方法,它们的主要区别在于是否需要一个目标对象(target object)以及它们生成代理对象的方式。

  1. CreateInterfaceProxyWithoutTarget

    • 这个方法用于创建一个代理对象,该对象不需要一个实际的目标对象实例。它在运行时动态生成一个实现了指定接口的对象,并且所有的方法调用都会被拦截器拦截。
    • 这意味着你可以为一个接口创建一个代理,而不需要实现该接口的任何类。
    • 如果接口方法要求返回值,你需要在拦截器中指定返回值。
  2. CreateInterfaceProxyWithTarget

    • 这个方法用于创建一个代理对象,该对象包装了一个已经存在的目标对象实例。
    • 它允许你为目标对象提供一个实现了接口的实例,并且所有的方法调用都会通过拦截器,然后转发到目标对象。
    • 这种方式下,目标对象的实现方法不需要是虚方法(virtual),因为代理对象不是通过继承来覆盖方法,而是通过委托来调用方法。

总结来说,CreateInterfaceProxyWithoutTarget 适用于不需要实际目标对象,而是希望动态创建一个实现接口的对象的场景,而 CreateInterfaceProxyWithTarget 适用于已经有了一个实现了接口的对象,希望通过代理来增强或修改其行为的场景。

public interface IPerson
{
	string FirstName { get; set; }
	string LastName { get; set; }
	void DoWork();
}

public class Person : IPerson
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
	
	public void DoWork()
	{
		Console.WriteLine("I am working!");
	}
}

不指定实现进行代理CreateInterfaceProxyWithoutTarget

var proxy = new ProxyGenerator()
   .CreateInterfaceProxyWithoutTarget<IPerson>(
      new Interceptor());
proxy.FirstName = "Scooby";
proxy.LastName = "Doo";

不指定实现进行代理的话不能调用Proceed,需要注释掉invocation.Proceed();

指定实现进行代理CreateInterfaceProxyWithTarget

只有Person被代理

var proxy = (IPerson)new ProxyGenerator()
   .CreateInterfaceProxyWithTarget(
      typeof(IPerson), 
      new Person(),
      new Interceptor());
proxy.FirstName = "Scooby";
proxy.LastName = "Doo";

CreateInterfaceProxyWithTargetInterface

Castle DynamicProxy 提供了 CreateInterfaceProxyWithTargetInterface 方法,用于创建接口代理对象。这个方法与 CreateInterfaceProxyWithTarget 相似,但有一些关键的区别:

  1. IChangeProxyTarget 接口支持

    • CreateInterfaceProxyWithTargetInterface 方法允许拦截器实现 IChangeProxyTarget 接口,这使得在拦截过程中可以动态地更改目标对象。这意味着在方法调用过程中,拦截器可以决定使用不同的目标对象来处理调用,而不是始终使用创建代理时指定的原始目标对象。
  2. 缓存使用

    • CreateInterfaceProxyWithTargetInterface 在使用缓存方面更为高效。它更好地利用了缓存机制,可以减少代理对象的创建次数,提高性能。这一点在性能敏感的应用中尤为重要。
  3. 类型共享

    • 根据搜索结果中的示例代码,使用 CreateInterfaceProxyWithTargetInterface 创建的代理对象会共享相同的类型,而使用 CreateInterfaceProxyWithTarget 创建的代理对象则不会。这可以通过比较两个代理对象的类型是否相等来验证。在示例中,使用 CreateInterfaceProxyWithTargetInterface 创建的两个代理对象的类型是相同的,而使用 CreateInterfaceProxyWithTarget 创建的两个代理对象的类型是不同的。

总结来说,CreateInterfaceProxyWithTargetInterface 提供了更灵活的拦截器支持和更高效的缓存使用,这使得它在需要动态更改目标对象或关注性能的场景中更为合适。而 CreateInterfaceProxyWithTarget 则适用于那些不需要动态更改目标对象的场景。

异步方法代理

使用Nuget简化异步代理

install-package Castle.Core.AsyncInterceptor

代理类

var generator2 = new ProxyGenerator();  
var interceptor2 = new AsyncInterceptor();  
var proxy2 = generator.CreateClassProxyWithTarget<AsyncModule>(new AsyncModule(), interceptor2);  
await proxy2.PerformAsyncAction();await proxy2.PerformAsyncFunc().Dump();

public class AsyncModule
{
	public virtual async Task PerformAsyncAction()
	{
		await Task.Delay(1000);
		Console.WriteLine("Async action performed.");
	}
	public virtual async Task<int> PerformAsyncFunc()
	{
		await Task.Delay(1000);
		return 10;
	}
}

拦截器

public class AsyncInterceptor : IAsyncInterceptor
{
	public void InterceptAsynchronous(Castle.DynamicProxy.IInvocation invocation)
	{
		// 在异步方法执行前的操作
		Console.WriteLine($"Before async method: {invocation.Method.Name}");

		// 调用异步方法
		invocation.Proceed();

		// 等待异步方法完成
		var task = (Task)invocation.ReturnValue;
		task.ContinueWith(t =>
		{
			// 在异步方法执行后的操作
			Console.WriteLine($"After async method: {invocation.Method.Name}");
		});
	}

	public void InterceptAsynchronous<TResult>(Castle.DynamicProxy.IInvocation invocation)
	{
		// 在异步方法执行前的操作
		Console.WriteLine($"Before async method: {invocation.Method.Name}");

		// 调用异步方法
		invocation.Proceed();

		// 等待异步方法完成
		var task = (Task<TResult>)invocation.ReturnValue;
		task.ContinueWith(t =>
		{
			// 在异步方法执行后的操作
			Console.WriteLine($"After async method: {invocation.Method.Name}");
		});
	}

	public void InterceptSynchronous(Castle.DynamicProxy.IInvocation invocation)
	{
		// 同步方法的拦截逻辑
		Console.WriteLine($"Before sync method: {invocation.Method.Name}");
		invocation.Proceed();
		Console.WriteLine($"After sync method: {invocation.Method.Name}");
	}
}

效果
![[Pasted image 20241113160528.png]]

代理接口

var generator = new ProxyGenerator();
var interceptor = new AsyncInterceptor();
var proxy = generator.CreateInterfaceProxyWithTargetInterface<IAsyncService>(new AsyncService(), interceptor);

// 调用异步方法,拦截器中的逻辑将被执行
await proxy.PerformAsyncAction();
await proxy.PerformAsyncFunc().Dump();


public interface IAsyncService
{
	Task PerformAsyncAction();
	Task<int> PerformAsyncFunc();
}

public class AsyncService : IAsyncService
{
	public async Task PerformAsyncAction()
	{
		await Task.Delay(1000);
		Console.WriteLine("Async action performed.");
	}
	public async Task<int> PerformAsyncFunc()
	{
		await Task.Delay(1000);
		return 10;
	}
}

效果
![[Pasted image 20241113160519.png]]


http://www.kler.cn/a/392485.html

相关文章:

  • MQTT协议解析 : 物联网领域的最佳选择
  • CentOS 服务
  • 2024年11月13日
  • 【金融风控】特征评估与筛选详解
  • Linux第四讲:Git gdb
  • LeetCode【0014】最长公共前缀
  • 15 个改变世界的开源项目:塑造现代技术的先锋力量
  • 在 .NET 6.0 中创建用于 CRUD 操作的 Web API
  • 上河AI上线ComfyUI工作台
  • 如何使用OpenCV和Python进行相机校准
  • Python+robotframework接口自动化测试实操(超详细总结)
  • 【Docker】Docker基础及docker-compose
  • 传奇996_19——龙岭总结
  • 最全最简单理解迭代器
  • HarmonyOS 如何实现传输中的数据加密
  • 《DiffusionDet: Diffusion Model for Object Detection》ICCV2023
  • Android Mobile Network Settings | APN 菜单加载异常
  • Docker如何以配置文件方式安装nginx
  • WPF应用设置参数存储
  • 【Linux】Linux环境基础开发工具使用(下)
  • C++入门基础知识148—【关于C++ 二元运算符重载】
  • C#语言:现代软件开发的核心工具
  • shardingsphere5.5.1基于spring体系的集成-数据脱敏04
  • uni-app表格带分页,后端处理过每页显示多少条
  • 深入理解接口测试:实用指南与最佳实践5.0(一)
  • Scala的List习题