前言

我们都知道,在.NET中实现动态代理AOP有多种方案,也有很多框架支持,但大多框架的实现原理都是通过Emit配合Activator一起使用,从IL级别上实现动态代理。

其实在.NET中有一个更为简单的方案可以实现动态代理,那就是DispatchProxy 类。

DispatchProxy 类

DispatchProxy 类是 .NET Core 2.1 引入的一种动态代理机制(.net frameowrk 早已支持),用于创建代理对象来处理实现了特定接口的目标对象的调用。这个类主要用于实现透明代理,允许你在调用目标对象的方法前后执行额外的逻辑,例如日志记录、性能监控等。

要使用 DispatchProxy 类,你需要创建一个继承自 DispatchProxy 的代理类,并实现一个抽象方法 Invoke,该方法会被调用以处理目标对象的方法调用。你可以在 Invoke 方法中编写额外的逻辑,然后使用 Create 方法来创建代理对象。

方法介绍

DispatchProxy 类主要有两个重要的方法,它们是 Create 和 Invoke。下面解释这两个方法的作用:

  1. Create 方法:

    • public static T Create<T,TProxy>() where TProxy : DispatchProxy
    • 这个方法用于创建一个实现了指定接口的代理对象。
    • 参数 T 是目标对象所实现的接口类型,而 TProxy 是继承自 DispatchProxy 的代理类类型。
    • Create 方法返回的对象类型是 T,即代理对象,必须是接口
    • 使用 Create 方法时,需要传入两个类型参数,其中 T 是接口类型,TProxy 是继承自 DispatchProxy 的代理类。
  2. Invoke 方法:

    • protected abstract object? Invoke(MethodInfo? targetMethod, object?[]? args);

    • 这个方法是一个抽象方法,需要在派生类中进行实现。

    • 当代理对象的方法被调用时,Invoke 方法会被调用,用于处理目标对象的方法调用。

    • 参数 targetMethod 是目标对象的方法信息,args 是方法调用时传入的参数数组。

    • Invoke 方法必须返回一个 object 对象,这是目标方法的返回值。

    • 在 Invoke 方法中,你可以编写额外的逻辑来处理方法调用,例如日志记录、性能监控等。

    • 如果代理对象的方法调用抛出了异常,可以在 Invoke 方法中捕获并处理这些异常。

总的来说,Create 方法用于创建代理对象,而 Invoke 方法用于处理目标对象的方法调用。使用这两个方法可以实现动态代理,对目标对象的方法调用进行拦截和处理,从而实现一些额外的功能,例如日志记录、权限验证等。

代码示例

如下代码是一个代理类(SubjectProxy)对接口(ISubject)的实现类(RealSubject)进行方法请求(Request)时,从而打印Console日志。

    using System;
using System.Reflection;
using System.Reflection.Emit; public class SubjectProxy : DispatchProxy
{
private ISubject _realSubject; protected override object Invoke(MethodInfo targetMethod, object[] args)
{
Console.WriteLine("Before invoking the real subject.");
var result = targetMethod.Invoke(_realSubject, args);
Console.WriteLine("After invoking the real subject.");
return result;
} public static ISubject Create(ISubject realSubject)
{
object proxy = Create<ISubject, SubjectProxy>();
((SubjectProxy)proxy)._realSubject = realSubject;
return (ISubject)proxy;
}
} public interface ISubject
{
void Request();
} public class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("RealSubject handles the request.");
}
} internal class Program
{
static void Main(string[] args)
{
ISubject realSubject = new RealSubject();
ISubject proxy = SubjectProxy.Create(realSubject);
proxy.Request();
}
}

具体输出如下:

Before invoking the real subject.
RealSubject handles the request.
After invoking the real subject.

如上代码中,实现对ISubject接口的代理,但实际场景中,需要代理的类肯定不止ISubject接口,如果我希望实现一个通用代理类,针对所有需要代理的实现类进行对应代理,该如何处理呢?

我们对如上代码进行基本优化

    using System;
using System.Reflection; public class GenericProxy<T> : DispatchProxy where T : class
{
private T _realObject; protected override object Invoke(MethodInfo targetMethod, object[] args)
{
Console.WriteLine($"GenericProxy Before invoking the real method {targetMethod.Name}");
var result = targetMethod.Invoke(_realObject, args);
Console.WriteLine($"GenericProxy After invoking the real method {targetMethod.Name}");
return result;
} public static T Create(T realObject)
{
object proxy = Create<T, GenericProxy<T>>();
((GenericProxy<T>)proxy)._realObject = realObject;
return (T)proxy;
}
} public interface ISubject01
{
void Request();
} public class RealSubject01 : ISubject01
{
public void Request()
{
Console.WriteLine("RealSubject01 handles the request.");
}
} public interface ISubject02
{
void Request();
} public class RealSubject02 : ISubject02
{
public void Request()
{
Console.WriteLine("RealSubject02 handles the request.");
}
} class Program
{
static void Main(string[] args)
{
ISubject01 realObject01 = new RealSubject01();
ISubject01 proxy01 = GenericProxy<ISubject01>.Create(realObject01);
proxy01.Request(); ISubject02 realObject02 = new RealSubject02();
ISubject02 proxy02 = GenericProxy<ISubject02>.Create(realObject02);
proxy02.Request(); Console.ReadLine();
}
}

如上图所示,可以将目标实现类,抽象为泛型T,这样就可以兼容多个实现类进行代理了。

输出结果:

GenericProxy Before invoking the real method  Request
RealSubject01 handles the request.
GenericProxy After invoking the real method Request
GenericProxy Before invoking the real method Request
RealSubject02 handles the request.
GenericProxy After invoking the real method Request

DispatchProxy 的优缺点

优点:

  1. 高级抽象:DispatchProxy 提供了一个更高级的抽象,允许你以更简单的方式来实现动态代理,而不需要直接与 IL(Intermediate Language,中间语言)交互。
  2. 简化开发:使用 DispatchProxy,你可以直接通过实现接口来定义代理类的行为,而不需要了解底层的 IL 编程。
  3. 可读性:由于 DispatchProxy 提供了一个更高级的抽象,生成的代理类更容易理解和阅读。

缺点:

  1. 只能基于接口: DispatchProxy 只能代理接口而不能代理类。抽象类,封闭类都是无法代理的,这意味着你必须针对你的类型定义一个接口。
  2. 只能代理接口方法:由于DispatchProxy类基于接口代理,所以无法代理某些特定的方法,如静态方法、私有方法等。
  3. 高级功能局限:由于DispatchProxy类是高级抽象,所以可扩展性偏弱,如果需要复杂代理还是得用Emit和Activator进行。

参考链接

DispatchProxy 类

.NET 代理模式(二) 动态代理-DispatchProxy的更多相关文章

  1. Java设计模式-代理模式之动态代理(附源代码分析)

    Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...

  2. (转)轻松学,Java 中的代理模式及动态代理

    背景:讲到反射机制,肯定会想到动态代理. 轻松学,Java 中的代理模式及动态代理 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.值得注意的是,代理类和被代理类应该 ...

  3. java代理模式及动态代理类

     1.      代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用 ...

  4. JAVA代理模式与动态代理模式

    1.代理模式 所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用.代理模式给某 ...

  5. Java的三种代理模式(Spring动态代理对象)

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  6. 动态代理模式——JDK动态代理

    今天,我就来讲一下动态代理的设计模式. 动态代理的意义在于生成一个代理对象,来代理真实对象,从而控制真实对象的访问.操作动态代理需要两个步骤:一.代理对象和真实对象建立代理关系.二.实现代理对象的代理 ...

  7. 《Java设计模式》之代理模式 -Java动态代理(InvocationHandler) -简单实现

    如题 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式可细分为如下, 本文不做多余解释 远程代理 虚拟代理 缓冲代理 保护代理 借鉴文章 ht ...

  8. 代理模式-jdk动态代理

    IDB package com.bjpowernode.proxy; /** * 代理类和目标类都必须使用同一个接口. */ public interface IDB { int insert(); ...

  9. Java代理模式之动态代理

    动态代理类的源码是程序在运行期间由JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件.代理角色和真实角色的联系在程序运行时确定! Java中有两种动态代理,一种是JDK自带的,另一种的CGL ...

  10. 代理模式与动态代理之JDK实现和CGlib实现

    静态代理 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. 由业务实现类.业务代理类 两部分组成.业务实现类 负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截.过滤.预处理, ...

随机推荐

  1. #左偏树,树形dp#洛谷 1552 [APIO2012]派遣

    题目 分析 那我指定管理层之后,选择薪水越小的人越好, 考虑小根堆,由于需要合并,所以采用左偏树 代码 #include <cstdio> #include <cctype> ...

  2. SQL CREATE INDEX 语句- 提高数据库检索效率的关键步骤

    SQL CREATE INDEX 语句 SQL CREATE INDEX 语句用于在表中创建索引. 索引用于比其他方式更快地从数据库中检索数据.用户无法看到索引,它们只是用于加速搜索/查询. 注意: ...

  3. Pytorch DistributedDataParallel(DDP)教程一:快速入门理论篇

    一. 写在前面 随着深度学习技术的不断发展,模型的训练成本也越来越高.训练一个高效的通用模型,需要大量的训练数据和算力.在很多非大模型相关的常规任务上,往往也需要使用多卡来进行并行训练.在多卡训练中, ...

  4. ArcMap分别求取矢量要素各区域的面积

      本文介绍基于ArcMap软件,自动批量计算矢量图层中各个要素的面积的方法.   一次,遇到一个问题,需要分别计算ArcMap软件中一个图层的所有面要素的面积.如图,这个图层中包括多个省级行政区矢量 ...

  5. maven 创建spring boot 需要的配置[一]

    前言 之所以写这个是因为现在官方推荐云创建: 所以标注一下maven project,创建后,如何导入spring boot. 正文 1.步骤一 在pom.xml 中加入: <dependenc ...

  6. Go 单元测试之mock接口测试

    目录 一.gomock 工具介绍 二.安装 三.使用 3.1 指定三个参数 3.2 使用命令为接口生成 mock 实现 3.3 使用make 命令封装处理mock 四.接口单元测试步骤 三.小黄书Se ...

  7. Vue3开源组件库

    最近收到的很多问题都是关于Vue3组件库的问题 今天就给大家推荐几个基于Vue3重构的开源组件库 目前状态都处于Beta阶段,建议大家抱着学习的心态入场,勿急于用到生产环境 Ant-design-vu ...

  8. React的理解以及特性

    一.是什么 React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案 遵循组件设计模式.声明式编程范式和函数式编程概念,以使前端应用程序更高效 使用虚拟DOM来有效地操作 ...

  9. JavaScript如何判断一个元素是否在可视区域中?

    一.用途 可视区域即我们浏览网页的设备肉眼可见的区域,如下图 在日常开发中,我们经常需要判断目标元素是否在视窗之内或者和视窗的距离小于一个值(例如 100 px),从而实现一些常用的功能,例如: 图片 ...

  10. 《c#高级编程》第4章C#4.0中的更改(九)——协变和逆变

    动态字典(Dynamic Dictionary)是指一个键值对集合,其中键和值的类型都可以在运行时确定并且可以动态变化.在C#中,可以使用 dynamic 关键字来实现这一功能. 例如,下面的代码演示 ...