动态代理配合 rpc 技术调用远程服务, 不用关注细节的实现, 让程序就像在本地调用以用.
因此动态代理在微服务系统中是不可或缺的一个技术. 网上看到大部分案例都是通过反射自己实现, 且相当复杂. 编写和调试相当不易, 我这里提供里一种简便的方式来实现动态代理.
1, 创建我们的空白. netcore 项目
通过 vs2017 轻易的创建出一个. netcore 项目
2, 编写 Startup.cs 文件
默认 Startup 没有构造函数, 自行添加构造函数, 带有 IConfiguration 参数的, 用于获取项目配置, 但我们的示例中未使用配置
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- // 注册编码提供程序
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- }
在 ConfigureServices 配置日志模块, 目前 core 更新的很快, 日志的配置方式和原来又很大出入, 最新的配置方式如下
- // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
- public void ConfigureServices(IServiceCollection services )
- {
- services.AddLogging(loggingBuilder=> {
- loggingBuilder.AddConfiguration(Configuration.GetSection("Logging"));
- loggingBuilder.AddConsole();
- loggingBuilder.AddDebug();
- });
- }
添加路由过滤器, 监控一个地址, 用于调用我们的测试代码. netcore 默认没有 UTF8 的编码方式, 所以要先解决 UTF8 编码问题, 否则将在输出中文时候乱码.
这里注意 Map 内部传递了参数 applicationBuilder , 千万不要 使用 Configure(IApplicationBuilder App, IHostingEnvironment env ) 中的 App 参数, 否则每次请求 API/health 时候都将调用这个中间件 (App.Run 会短路期后边所有的中间件),
- App.Map("/api/health", (applicationBuilder) =>
- {
- applicationBuilder.Run(context =>
- {
- return context.Response.WriteAsync(uName.Result, Encoding.UTF8);
- });
- });
3, 代理
netcore 已经为我们完成了一些工作, 提供了 DispatchProxy 这个类
- #region 程序集 System.Reflection.DispatchProxy, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
- // C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.App\2.2.0\ref\netcoreapp2.2\System.Reflection.DispatchProxy.dll
- #endregion
- namespace System.Reflection
- {
- //
- public abstract class DispatchProxy
- {
- //
- protected DispatchProxy();
- //
- // 类型参数:
- // T:
- //
- // TProxy:
- public static T Create<T, TProxy>() where TProxy : DispatchProxy;
- //
- // 参数:
- // targetMethod:
- //
- // args:
- protected abstract object Invoke(MethodInfo targetMethod, object[] args);
- }
- }
- View Code
这个类提供了一个实例方法, 一个静态方法:
- Invoke(MethodInfo targetMethod, object[] args) (注解 1)
- Create<T, TProxy>()(注解 2)
Create 创建代理的实例对象, 实例对象在调用方法时候会自动执行 Invoke
首先我们创建一个动态代理类 ProxyDecorator<T>:DispatchProxy 需要继承 DispatchProxy .
在 DispatchProxy 的静态方法 Create<T, TProxy>() 中要求 TProxy : DispatchProxy
ProxyDecorator 重写 DispatchProxy 的虚方法 invoke
我们可以在 ProxyDecorator 类中添加一些其他方法, 比如: 异常处理, MethodInfo 执行前后的处理.
重点要讲一下
ProxyDecorator 中需要传递一个 T 类型的变量 decorated .
因为 DispatchProxy.Create<T, ProxyDecorator<T>>(); 会创建一个新的 T 的实例对象, 这个对象是代理对象实例, 我们将 decorated 绑定到这个代理实例上
接下来这个代理实例在执行 T 类型的任何方法时候都会用到 decorated, 因为 [decorated] 会被传给 targetMethod.Invoke(decorated, args) (targetMethod 来自注解 1) ,invoke 相当于执行
这样说的不是很明白, 我们直接看代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
- namespace TestWFW
- {
- public class ProxyDecorator<T> : DispatchProxy
- {
- // 关键词 RealProxy
- private T decorated;
- private event Action<MethodInfo, object[]> _afterAction; // 动作之后执行
- private event Action<MethodInfo, object[]> _beforeAction; // 动作之前执行
- // 其他自定义属性, 事件和方法
- public ProxyDecorator()
- {
- }
- /// <summary>
- /// 创建代理实例
- /// </summary>
- /// <param name="decorated"> 代理的接口类型 </param>
- /// <returns></returns>
- public T Create(T decorated)
- {
- object proxy = Create<T, ProxyDecorator<T>>(); // 调用 DispatchProxy 的 Create 创建一个新的 T
- ((ProxyDecorator<T>)proxy).decorated = decorated; // 这里必须这样赋值, 会自动未 proxy 添加一个新的属性
- // 其他的请如法炮制
- return (T)proxy;
- }
- /// <summary>
- /// 创建代理实例
- /// </summary>
- /// <param name="decorated"> 代理的接口类型 </param>
- /// <param name="beforeAction"> 方法执行前执行的事件 </param>
- /// <param name="afterAction"> 方法执行后执行的事件 </param>
- /// <returns></returns>
- public T Create(T decorated, Action<MethodInfo, object[]> beforeAction, Action<MethodInfo, object[]> afterAction)
- {
- object proxy = Create<T, ProxyDecorator<T>>(); // 调用 DispatchProxy 的 Create 创建一个新的 T
- ((ProxyDecorator<T>)proxy).decorated = decorated;
- ((ProxyDecorator<T>)proxy)._afterAction = afterAction;
- ((ProxyDecorator<T>)proxy)._beforeAction = beforeAction;
- //((GenericDecorator<T>)proxy)._loggingScheduler = TaskScheduler.FromCurrentSynchronizationContext();
- return (T)proxy;
- }
- protected override object Invoke(MethodInfo targetMethod, object[] args)
- {
- if (targetMethod == null) throw new Exception("无效的方法");
- try
- {
- //_beforeAction 事件
- if (_beforeAction != null)
- {
- this._beforeAction(targetMethod, args);
- }
- object result = targetMethod.Invoke(decorated, args);
- System.Diagnostics.Debug.WriteLine(result); // 打印输出面板
- var resultTask = result as Task;
- if (resultTask != null)
- {
- resultTask.ContinueWith(task => //ContinueWith 创建一个延续, 该延续接收调用方提供的状态信息并执行 当目标系统 tasks.
- {
- if (task.Exception != null)
- {
- LogException(task.Exception.InnerException ?? task.Exception, targetMethod);
- }
- else
- {
- object taskResult = null;
- if (task.GetType().GetTypeInfo().IsGenericType &&
- task.GetType().GetGenericTypeDefinition() == typeof(Task<>))
- {
- var property = task.GetType().GetTypeInfo().GetProperties().FirstOrDefault(p => p.Name == "Result");
- if (property != null)
- {
- taskResult = property.GetValue(task);
- }
- }
- if (_afterAction != null)
- {
- this._afterAction(targetMethod, args);
- }
- }
- });
- }
- else
- {
- try
- {
- // _afterAction 事件
- if (_afterAction != null)
- {
- this._afterAction(targetMethod, args);
- }
- }
- catch (Exception ex)
- {
- //Do not stop method execution if exception
- LogException(ex);
- }
- }
- return result;
- }
- catch (Exception ex)
- {
- if (ex is TargetInvocationException)
- {
- LogException(ex.InnerException ?? ex, targetMethod);
- throw ex.InnerException ?? ex;
- }
- else
- {
- throw ex;
- }
- }
- }
- /// <summary>
- /// aop 异常的处理
- /// </summary>
- /// <param name="exception"></param>
- /// <param name="methodInfo"></param>
- private void LogException(Exception exception, MethodInfo methodInfo = null)
- {
- try
- {
- var errorMessage = new StringBuilder();
- errorMessage.AppendLine($"Class {decorated.GetType().FullName}");
- errorMessage.AppendLine($"Method {methodInfo?.Name} threw exception");
- errorMessage.AppendLine(exception.Message);
- //_logError?.Invoke(errorMessage.ToString()); 记录到文件系统
- }
- catch (Exception)
- {
- // ignored
- //Method should return original exception
- }
- }
- }
- }
代码比较简单, 相信大家都看的懂, 关键的代码和核心的系统代码已经加粗和加加粗 + 红 标注出来了
DispatchProxy<T> 类中有两种创建代理实例的方法
我们看一下第一种比较简单的创建方法
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder App, IHostingEnvironment env )
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- // 添加健康检查路由地址
- App.Map("/api/health", (applicationBuilder) =>
- {
- applicationBuilder.Run(context =>
- {
- IUserService userService = new UserService();
- // 执行代理
- var serviceProxy = new ProxyDecorator<IUserService>();
- IUserService user = serviceProxy.Create(userService); //
- Task<string> uName = user.GetUserName(222);
- context.Response.ContentType = "text/plain;charset=utf-8";
- return context.Response.WriteAsync(uName.Result, Encoding.UTF8);
- });
- });
- }
代码中 UserService 和 IUserService 是一个 class 和 interface 自己实现即可 , 随便写一个接口, 并用类实现, 任何一个方法就行, 下面只是演示调用方法时候会执行什么, 这个方法本身在岩石中并不重要.
由于 ProxyDecorator 中并未注入相关事件, 所以我们在调用 user.GetUserName(222) 时候看不到任何特别的输出. 下面我们演示一个相对复杂的调用方式.
- // 添加健康检查路由地址
- App.Map("/api/health2", (applicationBuilder) =>
- {
- applicationBuilder.Run(context =>
- {
- IUserService userService = new UserService();
- // 执行代理
- var serviceProxy = new ProxyDecorator<IUserService>();
- IUserService user = serviceProxy.Create(userService, beforeEvent, afterEvent); //
- Task<string> uName = user.GetUserName(222);
- context.Response.ContentType = "text/plain;charset=utf-8";
- return context.Response.WriteAsync(uName.Result, Encoding.UTF8);
- });
- });
IUserService user = serviceProxy.Create(userService, beforeEvent, afterEvent); 在创建代理实例时候传递了 beforEvent 和 afterEvent, 这两个事件处理
函数是我们在业务中定义的
- void beforeEvent(MethodInfo methodInfo, object[] arges)
- {
- System.Diagnostics.Debug.WriteLine("方法执行前");
- }
- void afterEvent(MethodInfo methodInfo, object[] arges)
- {
- System.Diagnostics.Debug.WriteLine("执行后的事件");
- }
在代理实例执行接口的任何方法的时候都会执行 beforeEvent, 和 afterEvent 这两个事件 (请参考 Invoke(MethodInfo targetMethod, object[] args) 方法的实现)
在我们的示例中将会在 vs 的 输出面板中看到
方法执行前
"方法输出的值"
执行后事件
我们看下运行效果图:
这是 user.GetUserName(222) 的运行结果
控制台输出结果
来源: https://www.cnblogs.com/netqq/p/11452374.html