動態(tài)代理配合rpc技術(shù)調(diào)用遠程服務(wù),不用關(guān)注細節(jié)的實現(xiàn),讓程序就像在本地調(diào)用以用。
因此動態(tài)代理在微服務(wù)系統(tǒng)中是不可或缺的一個技術(shù)。網(wǎng)上看到大部分案例都是通過反射自己實現(xiàn),且相當復(fù)雜。編寫和調(diào)試相當不易,我這里提供里一種簡便的方式來實現(xiàn)動態(tài)代理。
1、創(chuàng)建我們的空白.netcore項目?
通過vs2017輕易的創(chuàng)建出一個.netcore項目
2、編寫Startup.cs文件
默認Startup 沒有構(gòu)造函數(shù),自行添加構(gòu)造函數(shù),帶有??IConfiguration 參數(shù)的,用于獲取項目配置,但我們的示例中未使用配置
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(); }); }
添加路由過濾器,監(jiān)控一個地址,用于調(diào)用我們的測試代碼。netcore默認沒有UTF8的編碼方式,所以要先解決UTF8編碼問題,否則將在輸出中文時候亂碼。
這里注意?Map? 內(nèi)部傳遞了參數(shù)?applicationBuilder ,千萬不要 使用Configure(IApplicationBuilder
app, IHostingEnvironment env )
中的app參數(shù),否則每次請求api/health時候都將調(diào)用這個中間件(app.Run會短路期后邊所有的中間件),
app.Map("/api/health", (applicationBuilder) => {
applicationBuilder.Run(context=> { return
context.Response.WriteAsync(uName.Result, Encoding.UTF8); }); });
?
3、代理
netcore 已經(jīng)為我們完成了一些工作,提供了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(); // // 類型參數(shù): // T: // // TProxy: public static T
Create<T, TProxy>()where TProxy : DispatchProxy; // // 參數(shù): // targetMethod: //
// args: protected abstract object Invoke(MethodInfo targetMethod, object[]
args); } } View Code
這個類提供了一個實例方法,一個靜態(tài)方法:
Invoke(MethodInfo targetMethod, object[] args) (注解1)
Create<T, TProxy>()(注解2)
Create 創(chuàng)建代理的實例對象,實例對象在調(diào)用方法時候會自動執(zhí)行Invoke
?
?
首先我們創(chuàng)建一個動態(tài)代理類??ProxyDecorator<T>:DispatchProxy? 需要繼承DispatchProxy? 。
在DispatchProxy 的靜態(tài)方法??Create<T, TProxy>()中要求?TProxy : DispatchProxy
ProxyDecorator 重寫??DispatchProxy的虛方法invoke?
我們可以在ProxyDecorator 類中添加一些其他方法,比如:異常處理,MethodInfo執(zhí)行前后的處理。
重點要講一下?
ProxyDecorator 中需要傳遞一個 T類型的變量?decorated 。
因為??DispatchProxy.Create<T, ProxyDecorator<T>>(); 會創(chuàng)建一個新的T的實例對象?,
這個對象是代理對象實例,我們將?decorated 綁定到這個代理實例上
接下來這個代理實例在執(zhí)行T類型的任何方法時候都會用到?decorated,因為 [decorated]
會被傳給??targetMethod.Invoke(decorated, args)? ?(targetMethod 來自注解1) ,invoke相當于執(zhí)行
這樣說的不是很明白,我們直接看代碼
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 //關(guān)鍵詞 RealProxy 14 private T
decorated; 15 private event Action<MethodInfo, object[]> _afterAction; //動作之后執(zhí)行
16 private event Action<MethodInfo, object[]> _beforeAction; //動作之前執(zhí)行 17 18
//其他自定義屬性,事件和方法 19 public ProxyDecorator() 20 { 21 22 } 23 24 25 ///
<summary> 26 /// 創(chuàng)建代理實例 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>>(); //調(diào)用DispatchProxy
的Create 創(chuàng)建一個新的T 34 ((ProxyDecorator<T>)proxy).decorated = decorated;
//這里必須這樣賦值,會自動未proxy 添加一個新的屬性
//其他的請如法炮制 35
return (T)proxy; 36 } 37 38 /// <summary> 39 /// 創(chuàng)建代理實例 40 ///
</summary> 41 /// <param name="decorated">代理的接口類型</param> 42 /// <param
name="beforeAction">方法執(zhí)行前執(zhí)行的事件</param> 43 /// <param name="afterAction">
方法執(zhí)行后執(zhí)行的事件</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>>(); //
調(diào)用DispatchProxy 的Create 創(chuàng)建一個新的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 = resultas Task; 74 if (resultTask != null) 75 { 76
resultTask.ContinueWith(task =>//ContinueWith 創(chuàng)建一個延續(xù),該延續(xù)接收調(diào)用方提供的狀態(tài)信息并執(zhí)行 當目標系統(tǒng)
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()); 記錄到文件系統(tǒng) 151 } 152 catch (Exception)
153 { 154 // ignored 155 //Method should return original exception 156 } 157
}158 } 159 }
?
代碼比較簡單,相信大家都看的懂,關(guān)鍵的代碼和核心的系統(tǒng)代碼已經(jīng)加粗和加加粗+紅 標注出來了
DispatchProxy<T> 類中有兩種創(chuàng)建代理實例的方法
我們看一下第一種比較簡單的創(chuàng)建方法
// 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(); //執(zhí)行代理 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? 自己實現(xiàn)即可
,隨便寫一個接口,并用類實現(xiàn),任何一個方法就行,下面只是演示調(diào)用方法時候會執(zhí)行什么,這個方法本身在巖石中并不重要。
?由于ProxyDecorator 中并未注入相關(guān)事件,所以我們在調(diào)用 user.GetUserName(222)
時候看不到任何特別的輸出。下面我們演示一個相對復(fù)雜的調(diào)用方式。
// 添加健康檢查路由地址 app.Map("/api/health2", (applicationBuilder) => {
applicationBuilder.Run(context=> { IUserService userService = new UserService();
//執(zhí)行代理 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); 在創(chuàng)建代理實例時候傳遞了beforEvent 和 afterEvent,這兩個事件處理
函數(shù)是我們在業(yè)務(wù)中定義的
void beforeEvent(MethodInfo methodInfo, object[] arges)
{
System.Diagnostics.Debug.WriteLine("方法執(zhí)行前");
}
void afterEvent(MethodInfo methodInfo, object[] arges)
{
System.Diagnostics.Debug.WriteLine("執(zhí)行后的事件");
}
在代理實例執(zhí)行接口的任何方法的時候都會執(zhí)行? beforeEvent,和 afterEvent 這兩個事件(請參考Invoke(MethodInfo
targetMethod, object[] args) 方法的實現(xiàn))
在我們的示例中將會在vs的 輸出 面板中看到?
?
?
方法執(zhí)行前
“方法輸出的值”
執(zhí)行后事件
我們看下運行效果圖:
?
?
?
?這是?user.GetUserName(222) 的運行結(jié)果
?
?
?控制臺輸出結(jié)果
?
熱門工具 換一換