netcore 中的DispatchProxy 实现动态代理和AOP

9/4/2023 9:41:06 AM
440
0

动态代理配合rpc技术调用远程服务,不用关注细节的实现,让程序就像在本地调用以用。

因此动态代理在微服务系统中是不可或缺的一个技术。网上看到大部分案例都是通过反射自己实现,且相当复杂。编写和调试相当不易,.net提供里一种简便的方式来实现动态代理。

在更老的net framework框架中,官方提供了RealProxy 来实现动态代理,在net core 2.1以后的框架中,官方提供了DispatchProxy 来实现动态代理。
使用 DispatchProxy 创建代理类的过程更简单,不需要手动编写继承和重写的代码,而 RealProxy 需要手动编写继承和重写的代码。本文主要介绍DispatchProxy的使用。

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);
    }
}

这个类提供了一个实例方法,一个静态方法:

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相当于执行

这样说的不是很明白,我们直接看代码

 1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Linq.Expressions;
  5 using System.Reflection;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8 
  9 namespace TestWFW
 10 {
 11     public class ProxyDecorator<T> : DispatchProxy
 12     {
 13         //关键词 RealProxy
 14         private T decorated;
 15         private event Action<MethodInfo, object[]> _afterAction;   //动作之后执行
 16         private event Action<MethodInfo, object[]> _beforeAction;   //动作之前执行
 17 
 18         //其他自定义属性,事件和方法
 19         public ProxyDecorator()
 20         {
 21 
 22         }
 23 
 24 
 25         /// <summary>
 26         /// 创建代理实例
 27         /// </summary>
 28         /// <param name="decorated">代理的接口类型</param>
 29         /// <returns></returns>
 30         public T Create(T decorated)
 31         {
 32 
 33             object proxy = Create<T, ProxyDecorator<T>>();   //调用DispatchProxy 的Create  创建一个新的T
 34             ((ProxyDecorator<T>)proxy).decorated = decorated;       //这里必须这样赋值,会自动未proxy 添加一个新的属性
                                            //其他的请如法炮制
 35           return (T)proxy;
 36         }
 37 
 38         /// <summary>
 39         /// 创建代理实例
 40         /// </summary>
 41         /// <param name="decorated">代理的接口类型</param>
 42         /// <param name="beforeAction">方法执行前执行的事件</param>
 43         /// <param name="afterAction">方法执行后执行的事件</param>
 44         /// <returns></returns>
 45         public T Create(T decorated, Action<MethodInfo, object[]> beforeAction, Action<MethodInfo, object[]> afterAction)
 46         {
 47 
 48             object proxy = Create<T, ProxyDecorator<T>>();   //调用DispatchProxy 的Create  创建一个新的T
 49             ((ProxyDecorator<T>)proxy).decorated = decorated;
 50             ((ProxyDecorator<T>)proxy)._afterAction = afterAction;
 51             ((ProxyDecorator<T>)proxy)._beforeAction = beforeAction;
 52             //((GenericDecorator<T>)proxy)._loggingScheduler = TaskScheduler.FromCurrentSynchronizationContext();
 53             return (T)proxy;
 54         }
 55 
 56 
 57 
 58         protected override object Invoke(MethodInfo targetMethod, object[] args)
 59         {
 60             if (targetMethod == null) throw new Exception("无效的方法");
 61          
 62             try
 63             {
 64                 //_beforeAction 事件
 65                 if (_beforeAction != null)
 66                 {
 67                     this._beforeAction(targetMethod, args);
 68                 }
 69 
 70 
 71                 object result = targetMethod.Invoke(decorated, args);
 72              System.Diagnostics.Debug.WriteLine(result);      //打印输出面板
 73                 var resultTask = result as Task;
 74                 if (resultTask != null)
 75                 {
 76                     resultTask.ContinueWith(task =>    //ContinueWith   创建一个延续,该延续接收调用方提供的状态信息并执行 当目标系统 tasks。 
 77                     {
 78                         if (task.Exception != null)
 79                         {
 80                             LogException(task.Exception.InnerException ?? task.Exception, targetMethod);
 81                         }
 82                         else
 83                         {
 84                             object taskResult = null;
 85                             if (task.GetType().GetTypeInfo().IsGenericType &&
 86                                 task.GetType().GetGenericTypeDefinition() == typeof(Task<>))
 87                             {
 88                                 var property = task.GetType().GetTypeInfo().GetProperties().FirstOrDefault(p => p.Name == "Result");
 89                                 if (property != null)
 90                                 {
 91                                     taskResult = property.GetValue(task);
 92                                 }
 93                             }
 94                             if (_afterAction != null)
 95                             {
 96                                 this._afterAction(targetMethod, args);
 97                             }
 98                         }
 99                     });
100                 }
101                 else
102                 {
103                     try
104                     {
105                         // _afterAction 事件
106                         if (_afterAction != null)
107                         {
108                             this._afterAction(targetMethod, args);
109                         }
110                     }
111                     catch (Exception ex)
112                     {
113                         //Do not stop method execution if exception  
114                         LogException(ex);
115                     }
116                 }
117 
118                 return result;
119             }
120             catch (Exception ex)
121             {
122                 if (ex is TargetInvocationException)
123                 {
124                     LogException(ex.InnerException ?? ex, targetMethod);
125                     throw ex.InnerException ?? ex;
126                 }
127                 else
128                 {
129                     throw ex;
130                 }
131             }
132 
133         }
134 
135 
136         /// <summary>
137         /// aop异常的处理
138         /// </summary>
139         /// <param name="exception"></param>
140         /// <param name="methodInfo"></param>
141         private void LogException(Exception exception, MethodInfo methodInfo = null)
142         {
143             try
144             {
145                 var errorMessage = new StringBuilder();
146                 errorMessage.AppendLine($"Class {decorated.GetType().FullName}");
147                 errorMessage.AppendLine($"Method {methodInfo?.Name} threw exception");
148                 errorMessage.AppendLine(exception.Message);
149 
150                 //_logError?.Invoke(errorMessage.ToString());  记录到文件系统
151             }
152             catch (Exception)
153             {
154                 // ignored  
155                 //Method should return original exception  
156             }
157         }
158     }
159 }

 

代码比较简单,相信大家都看的懂,关键的代码和核心的系统代码已经加粗和加加粗+红 标注出来了

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的 输出 面板中看到 ,运行效果图

 

全部评论



提问