前言:
大家在寫程式時一定常常遇到要寫日誌,權限驗證....等等和主要邏輯不相干的事情
如果把上述這些動作寫在核心邏輯,會讓原有的程式碼變得雜亂
AOP(面向切面编程)可以有效的幫助我們解決上面問題,降低模塊間耦合度,理念來自於代理模式...
代理模式三部曲的最後一步要和大家介紹 [動態代理]
在上文和大家介紹 ProxyPattern代理模式(二) 靜態代理
靜態代理可以將執行邏輯和寫日誌這兩個動作分離乾淨
Q:但又衍生一個問題是我們有一大堆代理類別要寫,這樣好不方便
A:如果可以攔截或獲取方法實行的瞬間並在執行前後加上我們寫日誌的動作該有多好~~
聰明的.Net框架 已經幫我們處理上面問題了(不然我們要動態產生一堆程式碼和動態編譯他們...這會累屎人QQ)
何謂動態代理?
簡單來說就是
我簡單來使用.Net RealProxy 類別來實作,[透明動態代理]
要被RealProxy代理類別要符合以下一種情況
- 介面
- 繼承於
MarshalByRefObject
廢話不多說先附上程式碼
public class DynamicProxy<T> : RealProxy
where T : MarshalByRefObject
{
private MarshalByRefObject _target;
public DynamicProxy(T target) : base(typeof(T))
{
}
/// <summary>
/// 動態攔截方法實作的瞬間
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage callMethod = msg as IMethodCallMessage;
MethodInfo targetMethod = callMethod.MethodBase as MethodInfo;
IMethodReturnMessage returnMethod = null;
//得到方法上面的標籤 攔截要執行非核心邏輯的動作
var attrs = Attribute.GetCustomAttributes(targetMethod, typeof(AopBaseAttribute)) as AopBaseAttribute[];
try
{
foreach (var attr in attrs)
{
//執行方法前的動作
attr.Excuting(callMethod.Args);
}
//執行方法
var result = targetMethod.Invoke(_target, callMethod.Args);
returnMethod = new ReturnMessage(result,
callMethod.Args,
callMethod.Args.Length,
callMethod.LogicalCallContext,
callMethod);
foreach (var attr in attrs)
{
//執行方法後動作
attr.Excuted(result);
}
}
catch (Exception ex)
{
returnMethod = new ReturnMessage(ex, callMethod);
}
return returnMethod;
}
}
這是動態代理最核心的程式碼
- 繼承RealProxy類別並實作IMessage方法
public override IMessage Invoke(IMessage msg)
我們把上篇的靜態代理改成動態代理
首先我們為動態代理新增攔截點製作 AopBaseAttribute
為日後切面擴展程式的基礎
public abstract class AopBaseAttribute : Attribute, IInterception
{
public virtual void Excuted(object result)
{
}
public virtual void Excuting(object[] args)
{
}
}
新增LogAttribute
作為寫日誌的切入點
public class LogAttribute : AopBaseAttribute
{
public override void Excuting(object[] args)
{
var user = args.FirstOrDefault() as UserModel;
if (user != null)
{
Console.WriteLine($"DynamicProxy 使用者登入:帳號={user.UserName} 密碼={user.Password}");
}
Console.WriteLine();
}
}
邏輯類別那邊繼承 MarshalByRefObject
並將剛剛做的 Log標籤(Attirbute) 放在方法上
public class DLogicservice : MarshalByRefObject
{
private MockUserData userList = new MockUserData();
[Log]
public bool IsAuth(UserModel user)
{
return userList.GetAllUser()
.Any(o => user.UserName == o.UserName && user.Password == o.Password);
}
}
調用時只需要這樣
//產生代理類別
var proxy = new DynamicProxy<DLogicservice>(new DLogicservice());
//取得代理類別實體
var obj = proxy.GetTransparentProxy() as DLogicservice;
//呼叫方法
obj.IsAuth(testUser);
另外小弟有參考ASP.Net原始碼來製作動態的攔截器,放在github上面歡迎大家討論
如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^