Lind.DDD.Aspects通過Plugins實現方法的動態攔(lan)截~Lind里的AOP
.Net MVC之(zhi)(zhi)所以發展的(de)(de)如些之(zhi)(zhi)好,一個很重要原因就是它公(gong)開了一組AOP的(de)(de)過濾器,即使用(yong)這些過濾器可(ke)以方便的(de)(de)攔截(jie)controller里的(de)(de)action,并注(zhu)入我們自己的(de)(de)代碼邏輯(ji),向全局的(de)(de)異常記錄(lu),用(yong)戶(hu)授(shou)權(quan)(quan),Url授(shou)權(quan)(quan),操作(zuo)行為記錄(lu)等,這一大批Lind的(de)(de)基本組件都是實(shi)現MVC和API的(de)(de)過濾實(shi)現的(de)(de),使用(yong)這些過濾讓我們不(bu)用(yong)去像(xiang)HttpModule和HttpHandler那樣,還要在Config里配置注(zhu)入點,讓程(cheng)序(xu)員在開發方式上感覺(jue)很舒服,維護成功很低!
本文主要內容點
- Lind.DDD里的方法攔截器
- 動態注入需要Lind.DDD.Plugins的支持
- 零配置的方法攔截
- 一個日志攔截器
- 正在構建一個緩存攔截器
目錄結構

Lind.DDD里的方法攔截器
Lind.DDD.Aspects這個攔截器起源自ABP框架,但不知道為什么,ABP對這個攔截器并沒有完全實現,所以今天大叔又實現了一下,解決了相關BUG, 對方法攔截上,在動態代理工廠里對方法攔截(jie)上下文(wen)添加了一些必要的(de)參數,因(yin)為大叔認為,你只提供一個“方法名稱”參數,太過簡單(dan)了,哈哈。
/// <summary> /// 方法(fa)相關(guan)信息 /// </summary> public class MethodMetadata { /// <summary> /// 上下文(wen) /// </summary> private MethodInfo _context; /// <summary> /// 方(fang)法名 /// </summary> private string _methodName; public MethodMetadata(string methodName, MethodInfo context = null) { _methodName = methodName; _context = context; } /// <summary> /// 方法名(ming)稱 /// </summary> public virtual string MethodName { get { return _methodName; } set { _methodName = value; } } /// <summary> /// 方(fang)法上下(xia)文(wen) /// </summary> public virtual string Context { get { return _context; } set { _context = value; } } }
一個簡單的日志攔截器的實現,它(ta)在方(fang)法執行前去攔截
/// <summary> /// 方法執行前攔截(jie),并記錄日志 /// </summary> public class LoggerAspectAttribute : BeforeAspectAttribute { public override void Action(InvokeContext context) { Console.WriteLine("logger start!" + context.Method.MethodName); Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info(context.Method.MethodName + "這個(ge)方法開始執行"); } }
而(er)在程序中,這(zhe)(zhe)個特性Attribute如何被動態代理攔截(jie)呢,事件上,如果你直接寫代碼(ma)也是(shi)(shi)可(ke)以的(de),就是(shi)(shi)使用(yong)Aspect提供(gong)的(de)ProxyFactory工廠來進行生產(chan),但大叔認為,這(zhe)(zhe)樣的(de)代碼(ma)耦合度太高,而(er)且對于(yu)現有(you)的(de)代碼(ma)還需要進行修(xiu)改,最(zui)重(zhong)要一點(dian),這(zhe)(zhe)種代碼(ma)總感覺有(you)種壞味(wei)道(dao)!
static void Main(string[] args) { ProxyFactory.CreateProxy<ITest>(typeof(LoggerAspectTest)).Do(); Console.Read(); }
所(suo)以就有了下(xia)面大叔的(de)封(feng)裝,用(yong)到了Lind.DDD.Plugins這(zhe)(zhe)個插件模式,將所(suo)有的(de)攔(lan)截器都先進行注冊,然后在生產對(dui)象(xiang)時為(wei)它(ta)動態(tai)添(tian)加對(dui)應的(de)ProxyFactory對(dui)象(xiang),請大家(jia)接著向(xiang)下(xia)看,動態(tai)注入需要Lind.DDD.Plugins的(de)支持這(zhe)(zhe)部分講解(jie)。
動態注入需要Lind.DDD.Plugins的支持
上面的攔截器只是簡單的實現,簡單的調用,而不具有一般性,即你需要自己維護需要“攔截的代碼”,而大叔在進行使用中感覺很不爽,于是想起了Plugins,讓插件為我們實現這種注入,就像MVC的Filter一樣,在框(kuang)架本身去實現方(fang)法(fa)攔截的功能!大叔認為這樣(yang)才是最好的!
1 所(suo)有攔(lan)截器都(dou)繼承(cheng)(cheng)IAspectProxy表示(shi)接口,而它自己(ji)則(ze)是繼承(cheng)(cheng)IPlugins的(de)
/// <summary> /// 支(zhi)持AOP攔截的(de)接(jie)口,它被(bei)認為是一種插件動(dong)態注入(ru)到系統中 /// </summary> public interface IAspectProxy : Lind.DDD.Plugins.IPlugins { }
2 在PluginManager的Resolve方法中(zhong),添加動態的ProxyFactory實(shi)現(xian),讓(rang)實(shi)現(xian)了IAspectProxy的類型,自動進行攔(lan)截器(qi)的實(shi)現(xian)
/// <summary> /// 從(cong)插件容器(qi)里返回對象 /// </summary> /// <param name="serviceName">對(dui)象全名(ming)</param> /// <param name="serviceType">接口類型</param> /// <returns></returns> public static object Resolve(string serviceName, Type serviceType) { var obj = _container.ResolveNamed(serviceName, serviceType); if (typeof(Lind.DDD.Aspects.IAspectProxy).IsAssignableFrom(serviceType)) { obj = ProxyFactory.CreateProxy(serviceType, obj.GetType()); } return obj; }
OK,有了(le)上面(mian)的(de)代碼,我們(men)的(de)方(fang)法攔截就成了(le)一種插件(jian)了(le),在使用(yong)的(de)時間之(zhi)前的(de)插件(jian)的(de)使用(yong)方(fang)法相同,當然底層(ceng)還是使用(yong)autofac來實(shi)現的(de)Ioc容器。
var old = Lind.DDD.Plugins.PluginManager.Resolve<IAopHelloTest2>("Lind.DDD.UnitTest.AopHello"); old.Hello("zz", 1);
一個日志攔截器
日(ri)志(zhi)記錄是一些業務(wu)復雜方法(fa)必備的,如一些訂單方法(fa),用(yong)戶(hu)提現(xian)方法(fa)都會添(tian)加相(xiang)關(guan)的日(ri)志(zhi),而如果(guo)希望動態添(tian)加日(ri)志(zhi),而不在代碼段中(zhong)去添(tian)加,則可(ke)以設計一個日(ri)志(zhi)攔(lan)截器,當然你可(ke)以在方法(fa)執(zhi)行(xing)前去控(kong)制,也(ye)可(ke)以在方法(fa)執(zhi)行(xing)后去控(kong)制!
/// <summary> /// 方法執行前攔截(jie),并記錄日志 /// </summary> public class LoggerAspectAttribute : BeforeAspectAttribute { public override void Action(InvokeContext context) { Console.WriteLine("logger start!" + context.Method.MethodName); Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info(context.Method.MethodName + "這個方法開始執行"); } } /// <summary> /// 方(fang)法執(zhi)行完成后攔截,并記錄(lu)日志 /// </summary> public class LoggerEndAspectAttribute : AfterAspectAttribute { public override void Action(InvokeContext context) { Console.WriteLine("logger start!" + context.Method.MethodName); Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info(context.Method.MethodName + "這個方法開(kai)始執行"); } }
目錄方(fang)法(fa)需要(yao)(yao)添(tian)加(jia)這種日(ri)志的行為,只要(yao)(yao)在方(fang)法(fa)上添(tian)加(jia)對(dui)應的特性(xing)即可(ke),(方(fang)法(fa)不(bu)需要(yao)(yao)為虛方(fang)法(fa))而(er)不(bu)需要(yao)(yao)修改方(fang)法(fa)代(dai)碼體(ti),如下面的代(dai)碼
/// <summary> /// AOP調(diao)用方式 /// </summary> public class LoggerAspectTest : ITest { [LoggerAspectAttribute] public void Do() { //我(wo)做事情 Console.WriteLine("我做事情"); } }
正在構建一(yi)個緩存攔截(jie)器
目前,大叔(shu)(shu)正在(zai)構建一個緩存(cun)的(de)(de)攔截器,主(zhu)要是(shi)(shi)實(shi)現對方法返回值的(de)(de)緩存(cun),而不(bu)需要將這種緩存(cun)判斷(duan)的(de)(de)邏輯寫在(zai)每(mei)個方法體內,大叔(shu)(shu)認為,這種面向切(qie)面的(de)(de)AOP的(de)(de)設計,才(cai)是(shi)(shi)大勢(shi)所趨(qu),敬請大家期待!
/// <summary> /// 緩存攔截(jie)器(qi) /// </summary> public class CachingAspectAttribute : BeforeAspectAttribute { CachingMethod cachingMethod; public CachingAspectAttribute(CachingMethod cachingMethod) { this.cachingMethod = cachingMethod; } public override void Action(InvokeContext context) { var method = context.Method; string prefix = "Lind"; var baseInterfaces = context.GetType().GetInterfaces(); if (baseInterfaces != null && baseInterfaces.Any()) { foreach (var item in baseInterfaces) { prefix += item.ToString() + "_"; } } //鍵名(ming),在put和get時使用(yong) var key = prefix + method.MethodName; Console.WriteLine(key); switch (cachingMethod) { case CachingMethod.Remove: //…… break; case CachingMethod.Get: //…… break; case CachingMethod.Put: //…… break; default: throw new InvalidOperationException("無效的(de)緩(huan)存方式。"); } } }
我(wo)們對支(zhi)持的(de)追求將不會停(ting)止,希望廣(guang)大青年都可(ke)以踏一(yi)(yi)心來,去認真(zhen)的(de)研究一(yi)(yi)個技術,事實上,對一(yi)(yi)個技術研究透了,大叔(shu)認為(wei)就足夠了!
此致
敬禮