回到目錄 .Net MVC之所以發展的如些之好,一個很重要原因就是它公開了一組AOP的過濾器,即使用這些過濾器可以方便的攔截controller里的action,並註入我們自己的代碼邏輯,向全局的異常記錄,用戶授權,Url授權,操作行為記錄等,這一大批Lind的基本組件都是實現MVC和API的過濾實 ...
.Net MVC之所以發展的如些之好,一個很重要原因就是它公開了一組AOP的過濾器,即使用這些過濾器可以方便的攔截controller里的action,並註入我們自己的代碼邏輯,向全局的異常記錄,用戶授權,Url授權,操作行為記錄等,這一大批Lind的基本組件都是實現MVC和API的過濾實現的,使用這些過濾讓我們不用去像HttpModule和HttpHandler那樣,還要在Config里配置註入點,讓程式員在開發方式上感覺很舒服,維護成功很低!
本文主要內容點
- Lind.DDD里的方法攔截器
- 動態註入需要Lind.DDD.Plugins的支持
- 零配置的方法攔截
- 一個日誌攔截器
- 正在構建一個緩存攔截器
目錄結構
Lind.DDD里的方法攔截器
Lind.DDD.Aspects這個攔截器起源自ABP框架,但不知道為什麼,ABP對這個攔截器並沒有完全實現,所以今天大叔又實現了一下,解決了相關BUG, 對方法攔截上,在動態代理工廠里對方法攔截上下文添加了一些必要的參數,因為大叔認為,你只提供一個“方法名稱”參數,太過簡單了,哈哈。
/// <summary> /// 方法相關信息 /// </summary> public class MethodMetadata { /// <summary> /// 上下文 /// </summary> private MethodInfo _context; /// <summary> /// 方法名 /// </summary> private string _methodName; public MethodMetadata(string methodName, MethodInfo context = null) { _methodName = methodName; _context = context; } /// <summary> /// 方法名稱 /// </summary> public virtual string MethodName { get { return _methodName; } set { _methodName = value; } } /// <summary> /// 方法上下文 /// </summary> public virtual string Context { get { return _context; } set { _context = value; } } }
一個簡單的日誌攔截器的實現,它在方法執行前去攔截
/// <summary> /// 方法執行前攔截,並記錄日誌 /// </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 + "這個方法開始執行"); } }
而在程式中,這個特性Attribute如何被動態代理攔截呢,事件上,如果你直接寫代碼也是可以的,就是使用Aspect提供的ProxyFactory工廠來進行生產,但大叔認為,這樣的代碼耦合度太高,而且對於現有的代碼還需要進行修改,最重要一點,這種代碼總感覺有種壞味道!
static void Main(string[] args) { ProxyFactory.CreateProxy<ITest>(typeof(LoggerAspectTest)).Do(); Console.Read(); }
所以就有了下麵大叔的封裝,用到了Lind.DDD.Plugins這個插件模式,將所有的攔截器都先進行註冊,然後在生產對象時為它動態添加對應的ProxyFactory對象,請大家接著向下看,動態註入需要Lind.DDD.Plugins的支持這部分講解。
動態註入需要Lind.DDD.Plugins的支持
上面的攔截器只是簡單的實現,簡單的調用,而不具有一般性,即你需要自己維護需要“攔截的代碼”,而大叔在進行使用中感覺很不爽,於是想起了Plugins,讓插件為我們實現這種註入,就像MVC的Filter一樣,在框架本身去實現方法攔截的功能!大叔認為這樣才是最好的!
1 所有攔截器都繼承IAspectProxy表示介面,而它自己則是繼承IPlugins的
/// <summary> /// 支持AOP攔截的介面,它被認為是一種插件動態註入到系統中 /// </summary> public interface IAspectProxy : Lind.DDD.Plugins.IPlugins { }
2 在PluginManager的Resolve方法中,添加動態的ProxyFactory實現,讓實現了IAspectProxy的類型,自動進行攔截器的實現
/// <summary> /// 從插件容器里返回對象 /// </summary> /// <param name="serviceName">對象全名</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,有了上面的代碼,我們的方法攔截就成了一種插件了,在使用的時間之前的插件的使用方法相同,當然底層還是使用autofac來實現的Ioc容器。
var old = Lind.DDD.Plugins.PluginManager.Resolve<IAopHelloTest2>("Lind.DDD.UnitTest.AopHello"); old.Hello("zz", 1);
一個日誌攔截器
日誌記錄是一些業務複雜方法必備的,如一些訂單方法,用戶提現方法都會添加相關的日誌,而如果希望動態添加日誌,而不在代碼段中去添加,則可以設計一個日誌攔截器,當然你可以在方法執行前去控制,也可以在方法執行後去控制!
/// <summary> /// 方法執行前攔截,並記錄日誌 /// </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> /// 方法執行完成後攔截,並記錄日誌 /// </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 + "這個方法開始執行"); } }
目錄方法需要添加這種日誌的行為,只要在方法上添加對應的特性即可,(方法不需要為虛方法)而不需要修改方法代碼體,如下麵的代碼
/// <summary> /// AOP調用方式 /// </summary> public class LoggerAspectTest : ITest { [LoggerAspectAttribute] public void Do() { //我做事情 Console.WriteLine("我做事情"); } }
正在構建一個緩存攔截器
目前,大叔正在構建一個緩存的攔截器,主要是實現對方法返回值的緩存,而不需要將這種緩存判斷的邏輯寫在每個方法體內,大叔認為,這種面向切麵的AOP的設計,才是大勢所趨,敬請大家期待!
/// <summary> /// 緩存攔截器 /// </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() + "_"; } } //鍵名,在put和get時使用 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("無效的緩存方式。"); } } }
我們對支持的追求將不會停止,希望廣大青年都可以踏一心來,去認真的研究一個技術,事實上,對一個技術研究透了,大叔認為就足夠了!
此致
敬禮