一、AOP概念
官方解釋:AOP(Aspect-Oriented Programming,面向切面編程),它是可以通過預編譯方式和運行期動態代理實作在不修改源代碼的情況下給程式動態統一添加功能的一種技術,它是一種新的方法論,是對傳統OOP編程的一種補充,OOP是關注將需求功能劃分為不同的并且相對獨立、封裝良好的類,并讓它們有著屬于自己的行為,依靠繼承和多型等來定義彼此的關系;AOP是希望能夠將通用需求功能從不相關的類當中分離出來,能夠使得很多類共享一個行為,一旦發生變化,不必修改很多類,而只需要修改這個行為即可,AOP是使用切面(aspect)將橫切關注點模塊化,OOP是使用類將狀態和行為模塊化,在OOP的世界中,程式都是通過類和介面組織的,使用它們實作程式的核心業務邏輯是十分合適,但是對于實作橫切關注點(跨越應用程式多個模塊的功能需求)則十分吃力,比如日志記錄、權限驗證、例外攔截等,
個人理解:AOP就是將公用功能提取出來,如果以后公用功能的需求發生變化,只需要改動公用模塊的代碼即可,多個呼叫的地方則不需要改動,所謂面向切面,就是只關注通用功能,而不關注業務邏輯,它實作的方式一般是通過攔截,比如,專案中一般都有權限驗證的功能,進入每個頁面前都會驗證當前登錄用戶是否有權限查看該界面,我們不可能說在每個頁面的初始化方法里面都去寫這段驗證的代碼,這個時候我們的AOP就派上用場了,AOP的機制是預先定義一組特性,使它具有攔截方法的功能,可以讓你在執行方法之前和之后做你想做的業務,而我們使用的時候只需要在對應的方法或者類定義上面加上某一個特性就好了,
二、AOP優勢
1)將通用功能從業務邏輯中抽離出來,可以省略大量的重復代碼,有利于代碼的操作和維護,
2)在軟體設計時,抽出通用功能(切面),有利于軟體設計的模塊化,降低軟體架構的復雜度,也就是說通用的功能都是一個個單獨的模塊,在專案的主業務里面是看不到這些通用功能的設計代碼的,
三、AOP應用
3.1、靜態代理方式
3.1.1、使用裝飾器模式實作靜態代理
1)新建一個類:DecoratorAOP.cs
/// <summary> /// 使用裝飾器模式實作靜態代理 /// </summary> public class DecoratorAOP { /// <summary> /// 用戶類 /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// 用戶注冊介面 /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// 用戶注冊介面實作類 /// </summary> public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine($"用戶注冊成功,Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// 裝飾器模式實作AOP功能 /// </summary> public class UserProcessorDecorator : IUserProcessor { private IUserProcessor UserProcessor { get; set; } public UserProcessorDecorator(IUserProcessor userProcessor) { UserProcessor = userProcessor; } public void RegUser(User user) { PreProceed(user); UserProcessor.RegUser(user); PostProceed(user); } public void PreProceed(User user) { Console.WriteLine("方法執行前"); } public void PostProceed(User user) { Console.WriteLine("方法執行后"); } } /// <summary> /// 運行測驗 /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; IUserProcessor processor = new UserProcessorDecorator(new UserProcessor()); processor.RegUser(user); } }View Code
2)呼叫:
static void Main(string[] args) { #region 使用裝飾器模式實作靜態代理 DecoratorAOP.Show(); Console.Read(); #endregion }View Code
3)運行結果如下:

上面代碼是模擬用戶注冊的例子:注冊資訊提交前,需要做一些準備作業,比如資料有效性校驗等;注冊資訊提交后,還需要做日志記錄等,從上面的代碼可以看出,我們通過靜態植入的方式,手動地在執行方法前和執行方法后讓它做一些我們需要的功能,
3.1.2、使用代理模式實作靜態代理
1)新建一個類:ProxyAOP.cs
/// <summary> /// 使用代理模式實作靜態代理 /// </summary> public class ProxyAOP { /// <summary> /// 用戶類 /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// 用戶注冊介面 /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// 用戶注冊介面實作類 /// </summary> public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine($"用戶注冊成功,Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// 代理模式實作AOP功能 /// </summary> public class UserProcessorProxy : IUserProcessor { private IUserProcessor userProcessor = new UserProcessor(); public void RegUser(User user) { PreProceed(user); userProcessor.RegUser(user); PostProceed(user); } private void PreProceed(User user) { Console.WriteLine("方法執行前"); } private void PostProceed(User user) { Console.WriteLine("方法執行后"); } } public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; IUserProcessor processor = new UserProcessorProxy(); processor.RegUser(user); } }View Code
2)呼叫:
static void Main(string[] args) { #region 使用代理模式實作靜態代理 ProxyAOP.Show(); Console.Read(); #endregion }View Code
3)運行結果如下:

3.2、動態代理方式
3.2.1、使用.Net Remoting/RealProxy實作動態代理
1)新建一個類:RealProxyAOP.cs
/// <summary> /// 使用.Net Remoting/RealProxy實作動態代理 /// Client - TransparentProxy - RealProxy - Target Object /// 局限在業務類必須是繼承自MarshalByRefObject型別 /// </summary> public class RealProxyAOP { /// <summary> /// 用戶類 /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// 用戶注冊介面 /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// 用戶注冊介面實作類 /// 必須繼承自MarshalByRefObject父類,否則無法生成, /// </summary> public class UserProcessor : MarshalByRefObject, IUserProcessor { public void RegUser(User user) { Console.WriteLine($"用戶注冊成功,Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// 真實代理:提供代理的基本功能 /// </summary> public class MyRealProxy<T> : RealProxy { private T _target; public MyRealProxy(T target) : base(typeof(T)) { _target = target; } public override IMessage Invoke(IMessage msg) { PreProceed(msg); IMethodCallMessage callMessage = (IMethodCallMessage)msg; object returnValue =https://www.cnblogs.com/atomy/p/ callMessage.MethodBase.Invoke(_target, callMessage.Args); PostProceed(msg); return new ReturnMessage(returnValue, new object[0], 0, null, callMessage); } public void PreProceed(IMessage msg) { Console.WriteLine("方法執行前"); } public void PostProceed(IMessage msg) { Console.WriteLine("方法執行后"); } } /// <summary> /// 透明代理:提供實際物件駐留在客戶端空間中的假象 /// </summary> public static class TransparentProxy { public static T Create<T>() { T instance = Activator.CreateInstance<T>(); MyRealProxy<T> realProxy = new MyRealProxy<T>(instance); T transparentProxy = (T)realProxy.GetTransparentProxy(); return transparentProxy; } } /// <summary> /// 運行測驗 /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; UserProcessor processor = TransparentProxy.Create<UserProcessor>(); processor.RegUser(user); } }View Code
2)呼叫:
static void Main(string[] args) { #region 使用.Net Remoting/RealProxy實作動態代理 RealProxyAOP.Show(); Console.Read(); #endregion }View Code
3)運行結果如下:

3.2.2、使用Castle\DynamicProxy實作動態代理
1)在NuGet中安裝Castle.Core,

2)新建一個類:CastleProxyAOP.cs
/// <summary> /// 使用Castle\DynamicProxy實作動態代理 /// 方法必須是虛方法 /// </summary> public class CastleProxyAOP { /// <summary> /// 用戶類 /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// 用戶注冊介面 /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// 用戶注冊介面實作類 /// </summary> public class UserProcessor : IUserProcessor { /// <summary> /// 必須帶上virtual,否則無效, /// </summary> /// <param name="user"></param> public virtual void RegUser(User user) { Console.WriteLine($"用戶注冊成功,Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// 攔截器 /// </summary> public class MyInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { PreProceed(invocation); invocation.Proceed(); PostProceed(invocation); } public void PreProceed(IInvocation invocation) { Console.WriteLine("方法執行前"); } public void PostProceed(IInvocation invocation) { Console.WriteLine("方法執行后"); } } /// <summary> /// 運行測驗 /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; ProxyGenerator generator = new ProxyGenerator(); MyInterceptor interceptor = new MyInterceptor(); UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor); userprocessor.RegUser(user); } }View Code
3)呼叫:
static void Main(string[] args) { #region 使用Castle\DynamicProxy實作動態代理 CastleProxyAOP.Show(); Console.Read(); #endregion }View Code
4)運行結果如下:

3.2.3、使用EntLib\PIAB Unity實作AOP(非配置)
1)在NuGet中安裝Unity及Unity.Interception,

2)新建一個類:UnityAOP.cs
/// <summary> /// 使用EntLib\PIAB Unity實作動態代理(非配置) /// </summary> public class UnityAOP { #region 業務 /// <summary> /// 用戶類 /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// 用戶注冊介面 /// </summary> [ExceptionHandler(Order = 1)] [LogHandler(Order = 2)] [UserHandler(Order = 3)] [AfterLogHandler(Order = 5)] public interface IUserProcessor { void RegUser(User user); } /// <summary> /// 用戶注冊介面實作類 /// </summary> public class UserProcessor : IUserProcessor //可以不繼承MarshalByRefObject類 { public void RegUser(User user) { Console.WriteLine($"用戶注冊成功,Name:{user.Name} Password:{user.Password}"); } } #endregion 業務 #region 特性 /// <summary> /// 例外處理特性 /// </summary> public class ExceptionHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new ExceptionHandler() { Order = Order }; } } /// <summary> /// 日志處理特性 /// </summary> public class LogHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new LogHandler() { Order = Order }; } } /// <summary> /// 用戶資訊特性 /// </summary> public class UserHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { ICallHandler handler = new UserHandler() { Order = Order }; return handler; } } /// <summary> /// 后續日志特性 /// </summary> public class AfterLogHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new AfterLogHandler() { Order = Order }; } } #endregion 特性 #region 特性對應的行為 public class ExceptionHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); if (methodReturn.Exception == null) { Console.WriteLine("ExceptionHandler:沒有例外"); } else { Console.WriteLine($"ExceptionHandler:出現例外:{methodReturn.Exception.Message}"); } return methodReturn; } } public class LogHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { User user = input.Inputs[0] as User; string message = string.Format($"Name:{user.Name} Password:{user.Password}"); Console.WriteLine($"LogHandler:日志已記錄,Message:{message}"); IMethodReturn methodReturn = getNext()(input, getNext); return methodReturn; } } public class UserHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { User user = input.Inputs[0] as User; if (user.Password.Length < 10) { return input.CreateExceptionMethodReturn(new Exception("UserHandler:密碼長度不能小于10位")); } //getNext()(input, getNext):委托后的委托,即多重委托, IMethodReturn methodReturn = getNext()(input, getNext); return methodReturn; } } public class AfterLogHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); Console.WriteLine($"AfterLogHandler:方法執行結果--{methodReturn.ReturnValue}"); Console.WriteLine("AfterLogHandler:方法執行后"); return methodReturn; } } #endregion 特性對應的行為 /// <summary> /// 運行測驗 /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "HelloWorld" }; IUnityContainer container = new UnityContainer(); //宣告一個容器 container.AddNewExtension<Interception>() .RegisterType<IUserProcessor, UserProcessor>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>()); //顯式攔截 IUserProcessor processor = container.Resolve<IUserProcessor>(); processor.RegUser(user); //呼叫 } }View Code
3)呼叫:
static void Main(string[] args) { #region 使用EntLib\PIAB Unity實作動態代理(非配置) UnityAOP.Show(); Console.Read(); #endregion }View Code
4)運行結果如下:

3.2.4、使用EntLib\PIAB Unity實作AOP(帶配置)
1)繼續在NuGet中安裝Unity.Configuration、Unity.Interception.Configuration及Newtonsoft.Json,


2)分別建立以下類:
/// <summary> /// 用戶類 /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } }Entity.cs(用戶物體類)
/// <summary> /// 用戶注冊介面 /// </summary> public interface IUserProcessor { void RegUser(User user); }IUserProcessor.cs(用戶注冊介面)
/// <summary> /// 用戶注冊介面實作類 /// </summary> public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine($"用戶注冊成功,Name:{user.Name} Password:{user.Password}"); } }UserProcessor.cs(用戶注冊介面實作類)
/// <summary> /// 使用EntLib\PIAB Unity實作動態代理(帶配置) /// </summary> public class UnityConfigAOP { public static void Show() { User user = new User() { Name = "Hello", Password = "HelloWorld" }; //配置UnityContainer IUnityContainer container = new UnityContainer(); ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap { ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + @"UnityConfigAOP\Unity.Config") }; Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); configSection.Configure(container, "AOPContainer"); IUserProcessor processor = container.Resolve<IUserProcessor>(); processor.RegUser(user); } }UnityConfigAOP.cs(運行測驗)
/// <summary> /// 不需要特性 /// </summary> public class ExceptionBehavior : IInterceptionBehavior { public bool WillExecute { get { return true; } } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); Console.WriteLine("ExceptionBehavior"); if (methodReturn.Exception == null) { Console.WriteLine("無例外"); } else { Console.WriteLine($"例外:{methodReturn.Exception.Message}"); } return methodReturn; } }ExceptionBehavior.cs(例外處理類)
/// <summary> /// 不需要特性 /// </summary> public class CachingBehavior : IInterceptionBehavior { private static Dictionary<string, object> CachingBehaviorDictionary = new Dictionary<string, object>(); public bool WillExecute { get { return true; } } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Console.WriteLine("CachingBehavior"); string key = $"{input.MethodBase.Name}_{Newtonsoft.Json.JsonConvert.SerializeObject(input.Inputs)}"; if (CachingBehaviorDictionary.ContainsKey(key)) { return input.CreateMethodReturn(CachingBehaviorDictionary[key]); //斷路器,直接回傳, } else { IMethodReturn result = getNext().Invoke(input, getNext); if (result.ReturnValue != null) CachingBehaviorDictionary.Add(key, result.ReturnValue); return result; } } }CachingBehavior.cs(快取處理類)
/// <summary> /// 不需要特性 /// </summary> public class PermissionBehavior : IInterceptionBehavior { public bool WillExecute { get { return true; } } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Console.WriteLine("PermissionBehavior"); Console.WriteLine(input.MethodBase.Name); foreach (var item in input.Inputs) { Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item)); //反射&序列化獲取更多資訊 } return getNext().Invoke(input, getNext); } }PermissionBehavior.cs(權限處理類)
/// <summary> /// 不需要特性 /// </summary> public class ParameterCheckBehavior : IInterceptionBehavior { public bool WillExecute { get { return true; } } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { User user = input.Inputs[0] as User; //可以不寫死型別,反射+特性完成資料有效性監測, Console.WriteLine("ParameterCheckBehavior"); if (user.Password.Length < 10) //可以過濾一下敏感詞 { return input.CreateExceptionMethodReturn(new Exception("密碼長度不能小于10位")); } else { return getNext().Invoke(input, getNext); } } }ParameterCheckBehavior.cs(引數檢測類)
/// <summary> /// 不需要特性 /// </summary> public class LogBehavior : IInterceptionBehavior { public bool WillExecute { get { return true; } } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); //執行后面的全部動作 Console.WriteLine("LogBehavior"); Console.WriteLine(input.MethodBase.Name); foreach (var item in input.Inputs) { Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item)); //反射&序列化獲取更多資訊 } return methodReturn; } }LogBehavior.cs(日志處理類)
3)新建一個組態檔Unity.Config(本例代碼是在UnityConfigAOP檔案夾下),在其屬性的復制到輸出目錄項下選擇始終復制,
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
</configSections>
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers>
<container name="AOPContainer">
<extension type="Interception"/>
<!--注冊匹配規則:前面是完整型別名稱,后面是所在的dll名稱,-->
<register type="LinkTo.Test.ConsoleAop.UnityConfigAOP.IUserProcessor,LinkTo.Test.ConsoleAop" mapTo="LinkTo.Test.ConsoleAop.UnityConfigAOP.UserProcessor,LinkTo.Test.ConsoleAop">
<interceptor type="InterfaceInterceptor"/>
<!--攔截順序為由上而下;配置會全部執行,除非遇到斷路器等;建議例外處理包在最外層,即在最上面,-->
<interceptionBehavior type="LinkTo.Test.ConsoleAop.UnityConfigAOP.ExceptionBehavior, LinkTo.Test.ConsoleAop"/>
<interceptionBehavior type="LinkTo.Test.ConsoleAop.UnityConfigAOP.CachingBehavior, LinkTo.Test.ConsoleAop"/>
<interceptionBehavior type="LinkTo.Test.ConsoleAop.UnityConfigAOP.PermissionBehavior, LinkTo.Test.ConsoleAop"/>
<interceptionBehavior type="LinkTo.Test.ConsoleAop.UnityConfigAOP.ParameterCheckBehavior, LinkTo.Test.ConsoleAop"/>
<interceptionBehavior type="LinkTo.Test.ConsoleAop.UnityConfigAOP.LogBehavior, LinkTo.Test.ConsoleAop"/>
</register>
</container>
</containers>
</unity>
</configuration>
Unity.Config
4)呼叫:
static void Main(string[] args) { #region 使用EntLib\PIAB Unity實作動態代理(帶配置) UnityConfigAOP.UnityConfigAOP.Show(); Console.Read(); #endregion }View Code
5)運行結果如下:

3.3、IL編織方式
IL編織方式,可以使用PostSharp框架來做,但是由于Postsharp從2.0版本開始收費,此處不再作說明,有興趣的話可以百度一下,
參考自:
https://www.cnblogs.com/landeanfen/p/4782370.html
https://www.cnblogs.com/artech/archive/2011/12/01/autointerception.html
E神公開課代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/59592.html
標籤:C#
