一、AOP概念 官方解釋:AOP(Aspect-Oriented Programming,面向切麵編程),它是可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程式動態統一添加功能的一種技術。它是一種新的方法論,是對傳統OOP編程的一種補充。OOP是關註將需求功能劃分為不同的並且相對獨立 ...
一、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 = 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 =