20181122_C#中AOP_使用Unity實現AOP

一.   使用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>
View Code

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 }
View Code

e)  IuserProcessor代碼:this

1 public interface IUserProcessor
2     {
3         void RegUser(User user);
4         User GetUser(User user);
5 }
View Code

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 }
View Code

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     }
View Code

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     }
View Code

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     }
View Code

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     }
View Code

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     }
View Code

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     }
View Code
相關文章
相關標籤/搜索