一. 使用Unity的AOP實現緩存
a) 總體項目截圖:app
b) 添加Unity的Nuget包, 直接使用最新版就行, 須要添加兩個 Unity 和 Unity.Interception (這個是爲AOP作的一個擴展) ide
c) AOP配置文件, 詳細註釋(CfgFiles\Unity.Config)函數
1 <!--這是一個標準Unity配置文件的格式--> 2 3 <configuration> <!--根節點名稱--> 4 <configSections> 5 <!--name容器的名稱 type 表示如何查找節點, 這是固定寫法--> 6 <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> 7 </configSections> 8 <unity> 9 <!--下面一行是固定寫法, 用這個寫法來支持AOP--> 10 <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/> 11 <containers><!--定義一組容器--> 12 <container name="aopContainer"> 13 <!--定義一個容器; 表示在IUserProcessor這個接口下全部的方法都給他增長, 這個容器(aopContainer)內所包含的方法--> 14 <extension type="Interception"/> 15 <!--這裏使用Interception類型的擴展; 其實在Unity中有三種類型來處理這件事; 這種是屬於實現接口的方法, 還有一種是繼承父類; 第三種是使用Virtual虛方法的形式, 使用虛方法有點相似於CastleProxyAOP這個代理--> 16 <!--把接口映射到具體的實現類; 也就是說告訴容器若是遇到了MyAOP.UnityWay.IUserProcessor這個抽象, 就使用MyAOP.UnityWay.UserProcessor來幫咱們實例化--> 17 <!--MyAOP.UnityWay.IUserProcessor完整的接口名稱, MyAOP是程序所在的dll名稱, 程序集的名稱(固然在這裏是當前exe文件的名稱--> 18 <!--MyAOP.UnityWay.UserProcessor具體的實現類, MyAOP和上面的解釋同樣--> 19 <!--MyAOP是程序集的名稱--> 20 <!-- register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP" 這一句總體的意思就是告訴容器, 若是遇到IUserProcessor這個抽象, 就使用UserProcessor類來幫我進行初始化一個具體的實例出來--> 21 <register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP"> 22 <!--Unity中的AOP有三種模式, 第一種必須繼承MarshalByRefObject這個父類的AOP 23 第二種必須是虛方法的AOP 24 第三種就是下面寫的接口形式的 InterfaceInterceptor 通常都用這種接口形式的: 25 表示只要是接口下的全部方法, 都會被增長上下面的方法 26 --> 27 <interceptor type="InterfaceInterceptor"/> <!--AOP支持的模式; 推薦使用這種接口模式的--> 28 <!--下面這五個表示 在IUserProcessor接口中的全部方法, 都擁有下面方法的擴展--> 29 30 <!--異常處理的; 注意若是你想全局處理異常, 則應該把異常處理的Behavior放到最頂層, 不然在Unity中本身拋出的異常則會抓不到--> 31 <interceptionBehavior type="MyAOP.UnityWay.ExceptionLoggingBehavior, MyAOP"/> 32 <!--緩存的behavior; 注意緩存的Behavior也應該放在記錄日誌以前; 須要注意的是, 若是被緩存命中了, 那麼後面的AOP也不會執行的--> 33 <interceptionBehavior type="MyAOP.UnityWay.CachingBehavior, MyAOP"/> 34 <!--方法執行前寫日誌--> 35 <interceptionBehavior type="MyAOP.UnityWay.LogBeforeBehavior, MyAOP"/> 36 <!--參數檢查--> 37 <interceptionBehavior type="MyAOP.UnityWay.ParameterCheckBehavior, MyAOP"/> 38 <!--方法執行後作的事情--> 39 <interceptionBehavior type="MyAOP.UnityWay.LogAfterBehavior, MyAOP"/> 40 </register> 41 </container> 42 </containers> 43 </unity> 44 </configuration>
d) Model代碼:ui
1 public class User 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 public string Password { get; set; } 6 }
e) IuserProcessor代碼:this
1 public interface IUserProcessor 2 { 3 void RegUser(User user); 4 User GetUser(User user); 5 }
f) UserProcessor代碼:spa
1 public class UserProcessor : IUserProcessor 2 { 3 public void RegUser(User user) 4 { 5 Console.WriteLine("用戶已註冊。"); 6 //throw new Exception("11"); 7 } 8 [Obsolete] 9 public User GetUser(User user) 10 { 11 return user; 12 } 13 }
g) LogBeforeBehavior代碼(在方法執行以前寫日誌):.net
1 /// <summary> 2 /// 1. 標準的經過AOP寫日誌的功能, 必須實現IInterceptionBehavior; 這個接口來自於Unity容器 3 /// </summary> 4 public class LogBeforeBehavior : IInterceptionBehavior 5 { 6 /// <summary> 7 /// 3. 此方法爲固定寫法 8 /// </summary> 9 /// <returns></returns> 10 public IEnumerable<Type> GetRequiredInterfaces() 11 { 12 return Type.EmptyTypes; 13 } 14 15 /// <summary> 16 /// 4. 關鍵方法 17 /// </summary> 18 /// <param name="input"></param> 19 /// <param name="getNext"></param> 20 /// <returns></returns> 21 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 22 { 23 Console.WriteLine("LogBeforeBehavior"); 24 //5. 若是有的方法想用, 有的方法不想被AOP, 則能夠在這裏作切斷 25 26 //這裏能夠將某個方法打上特性,而後獲取MemberInfo中的特性信息, 以此來判斷哪一個 27 //方法須要AOP切入,哪些方法不須要AOP切入 28 Console.WriteLine(input.MethodBase.Name); 29 // Console.WriteLine( input.MethodBase.GetCustomAttributes(true)[0].ToString() ); 30 foreach (var item in input.Inputs) //input中包含了全部的參數信息 31 { 32 Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item)); 33 //反射&序列化獲取更多信息 34 } 35 // Unity.Interception.InterceptionBehaviors.InvokeInterceptionBehaviorDelegate gn = getNext(); 36 // return getNext()(input, getNext);//注意這種寫法也是能夠的; 這種寫法的意思就是getNext()方法返回了一個委託, 而後委託又被調用了 37 return getNext().Invoke(input, getNext);//4. 固定寫法 38 39 // return null; 40 //return gn.Invoke(input, getNext); 41 //getNext()表示執行完當前的方法以後, 去執行原始方法; 可是注意的是, getNext()中可能有多個方法 42 } 43 44 45 /// <summary> 46 /// 2. 固定寫法 47 /// </summary> 48 public bool WillExecute 49 { 50 get { return true; } 51 } 52 }
h) LogAfterBehavior代碼(方法執行以後寫日誌):代理
1 //IInterceptionBehavior來自於AOP容器中 Unity.Interception.InterceptionBehaviors; 2 public class LogAfterBehavior : IInterceptionBehavior 3 { 4 /// <summary> 5 /// 固定寫法 6 /// </summary> 7 /// <returns></returns> 8 public IEnumerable<Type> GetRequiredInterfaces() 9 { 10 return Type.EmptyTypes; 11 } 12 13 /// <summary> 14 /// 能夠記錄調用時間, 參數, 函數名稱 15 /// </summary> 16 /// <param name="input"></param> 17 /// <param name="getNext"></param> 18 /// <returns></returns> 19 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 20 { 21 //getNext()(input, getNext); 關鍵點; 若是業務代碼寫在這句話以前, 那麼就會先執行業務代碼, 再執行配置文件中配置的實例代碼; 反之同理 22 23 IMethodReturn methodReturn = getNext().Invoke(input, getNext);//執行後面的所有動做 24 //原始方法執行後 25 26 Console.WriteLine("LogAfterBehavior"); 27 Console.WriteLine(input.MethodBase.Name); 28 foreach (var item in input.Inputs) 29 { 30 Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item)); 31 //反射&序列化獲取更多信息 32 } 33 Console.WriteLine("LogAfterBehavior" + methodReturn.ReturnValue); 34 return methodReturn; 35 } 36 37 /// <summary> 38 /// 固定寫法 39 /// </summary> 40 public bool WillExecute 41 { 42 get { return true; } 43 } 44 }
i) ParameterCheckBehavior代碼(在方法執行以前進行參數校驗):日誌
1 /// <summary> 2 /// 參數檢查 3 /// </summary> 4 public class ParameterCheckBehavior : IInterceptionBehavior 5 { 6 public IEnumerable<Type> GetRequiredInterfaces() 7 { 8 return Type.EmptyTypes; 9 } 10 11 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 12 { 13 Console.WriteLine("ParameterCheckBehavior"); 14 User user = input.Inputs[0] as User;//能夠不寫死類型,反射+特性完成數據有效性監測 15 if (user.Password.Length < 10)//能夠過濾一下敏感詞 16 { 17 //返回一個異常 18 return input.CreateExceptionMethodReturn(new Exception("密碼長度不能小於10位")); 19 //注意只要拋出異常, 那麼後面的都不會再執行了, 在這裏也就是說後的 logafterbehavior是不會再執行了 20 //throw new Exception("密碼長度不能小於10位"); 21 } 22 else 23 { 24 Console.WriteLine("參數檢測無誤"); 25 return getNext().Invoke(input, getNext); 26 } 27 } 28 29 public bool WillExecute 30 { 31 get { return true; } 32 } 33 }
j) ExceptionLoggingBehavior代碼(異常處理):
1 /// <summary> 2 /// 異常處理 3 /// </summary> 4 public class ExceptionLoggingBehavior : IInterceptionBehavior 5 { 6 public IEnumerable<Type> GetRequiredInterfaces() 7 { 8 return Type.EmptyTypes; 9 } 10 11 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 12 { 13 IMethodReturn methodReturn = getNext()(input, getNext); 14 15 Console.WriteLine("ExceptionLoggingBehavior"); 16 if (methodReturn.Exception == null) //檢查methodReturn中是否有異常 17 { 18 Console.WriteLine("無異常"); 19 } 20 else 21 { 22 Console.WriteLine($"異常:{methodReturn.Exception.Message}"); 23 } 24 return methodReturn; 25 } 26 27 public bool WillExecute 28 { 29 get { return true; } 30 } 31 }
k) CachingBehavior代碼(緩存處理):
1 /// <summary> 2 /// 緩存AOP 3 /// </summary> 4 public class CachingBehavior : IInterceptionBehavior 5 { 6 public IEnumerable<Type> GetRequiredInterfaces() 7 { 8 return Type.EmptyTypes; 9 } 10 /// <summary> 11 /// 定義緩存字典 12 /// </summary> 13 private static Dictionary<string, object> CachingBehaviorDictionary = new Dictionary<string, object>(); 14 15 public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) 16 { 17 Console.WriteLine("CachingBehavior"); 18 //把方法的名稱和全部的參數列表所有標識爲key , 而後放到字典裏面; 19 string key = $"{input.MethodBase.Name}_{Newtonsoft.Json.JsonConvert.SerializeObject(input.Inputs)}"; 20 //當方法名和參數都不變, 則表示有緩存 21 22 23 if (CachingBehaviorDictionary.ContainsKey(key)) 24 { 25 return input.CreateMethodReturn(CachingBehaviorDictionary[key]);//直接返回 [短路器] 再也不往下; 包括後面的Behavior也不會再執行了; 由於所有被直接短路了 26 } 27 else 28 { 29 //若是字典中沒有, 則繼續執行這裏 30 IMethodReturn result = getNext().Invoke(input, getNext); 31 if (result.ReturnValue != null) //並將其加入到緩存列表中去; 固然緩存中放一個null沒有任何意義, 因此這裏判斷一下 32 { 33 //不存在則, 加入緩存中 34 CachingBehaviorDictionary.Add(key, result.ReturnValue); 35 } 36 37 return result; 38 } 39 40 41 } 42 43 public bool WillExecute 44 { 45 get { return true; } 46 } 47 }
l) UnityConfigAOP代碼(調用):
1 /// <summary> 2 /// 使用EntLib\PIAB Unity 實現動態代理 3 /// 4 /// 1. 添加引用→右鍵→添加 NuGet包, 添加Unity的包引用 5 /// 2. 這裏添加的 Unity是5.8.13的版本, 而Unity.Interception是5.5.0; 由於最新的(5.5.5)不支持 .net 4.0 6 /// 3. Unity是一個 Unity的容器; 而Unity.Interception當Unity作AOP時的一個擴展 7 /// </summary> 8 public class UnityConfigAOP 9 { 10 public static void Show() 11 { 12 User user = new User() 13 { 14 Name = "孫悟空", 15 Password = "12345678934534643" 16 }; 17 18 19 { 20 //1. 初始化UnityContainer容器 21 IUnityContainer container = new UnityContainer(); 22 ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); 23 24 //2. 開始讀取配置文件 25 fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config"); 26 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); 27 UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); 28 29 30 //3. 使用配置文件中aopContainer節點下的全部配置信息來初始化container這個AOP容器 31 configSection.Configure(container, "aopContainer"); //10. 這個aopContainer就是配置文件中的 <container name="aopContainer"> 這裏這個容器的名稱 ; 注意若是配置文件中的名字和這裏的名字不同就會報出如下錯誤: 32 // The container named "aopContainer" is not defined in this configuration section. 33 34 // 在這裏建立對象, 建立的規則就來自於配置文件的 <register type="MyAOP.UnityWay.IUserProcessor,MyAOP" mapTo="MyAOP.UnityWay.UserProcessor,MyAOP"> 這一行; 若是是 IUserProcessor 類型, 就是使用UserProcessor 來建立實例 35 IUserProcessor processor = container.Resolve<IUserProcessor>(); 36 processor.RegUser(user); //4. 注意, 當程序運行到這裏的時候, 正常狀況應該是去調用userProcessor的Reguser方法, 可是因爲在配置文件中進行了配置, 因此它會去執行 LogBeforeBehavior 的 Invoke的方法(當aopContainer這容器中只有一個LogBeforeBehavior的時候, 若是多個, 則那個方法在前面則優先執行, 其它的上依次的調) 37 User userNew1 = processor.GetUser(user); //調用GetUser的時候, 也會執行配置文件中對應配置了的方法 38 39 40 //演示緩存Behavior 41 User userNew2 = processor.GetUser(user); 42 } 43 } 44 }