AOP(面向切面編程)大概瞭解一下

前言

上一篇在聊MemoryCache的時候,用到了Autofac提供的攔截器進行面向切面編程,很明顯能體會到其優點,既然涉及到了,那就趁熱打鐵,一塊兒來探探面向切面編程。git

正文

1. 概述

在軟件業,AOPAspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期間動態代理實現程序功能統一維護的一種技術。AOP是OOP(面向對象程序設計)的延續,是軟件開發中的一個熱點,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。程序員

---來自百度百科github

總結優勢數據庫

  • 對業務邏輯的各個部分進行隔離,業務之間耦合度下降;
  • 提升程序的可重用性,同時程序更容易維護;
  • 提升開發效率,不用花大量的時間在業務中增長代碼,還能下降風險;

其實AOP的本質就是動態代理,何爲動態代理呢?編程

動態代理就是在程序運行時,建立目標對象的代理對象,並對目標對象中的方法進行功能性加強的一種技術。通俗一點來講就是在運行期間對方法的攔截,在方法執行先後進行額外的業務處理,從而在不嵌入原有代碼邏輯狀況下就能加強被攔截方法的業務能力。緩存

理論先到這,一塊兒來看看用代碼怎麼實現吧?安全

2. 實踐檢驗真理(到底優不優秀)

先來一個控制檯項目,什麼都沒有,從頭開始擼代碼,先來看看項目結構:ide

圖片

老案例了,仍是僞裝在進行用戶維護,模擬對用戶進行增刪改查。此次就直接上代碼啦啊,根據項目結構依次看看代碼:函數式編程

  • 在AopModel中增長User.cs函數

    public class User
    {
      public string Name { get; set; }
      public int Age { get; set; }
    }
  • 在AopService中增長IUser.cs和User.cs

    IUserService.cs

    public interface IUserService
    {
      bool AddUser(User user);
    }

    UserService.cs

    public class UserService : IUserService
    {
      public bool AddUser(User user)
      {
          Console.WriteLine("用戶添加成功");
          return true;
      }
    }
  • Main方法

    class Program
    {
      static void Main(string[] args)
      {
          Console.WriteLine("========原始需求=========");
          User user = new User { Name = "Zoe", Age = 18 };
          IUserService userService = new UserService();
          // 模擬增長一個用戶
          userService.AddUser(user);
          Console.ReadLine();
      }
    }

這樣項目就正常運行啦,這個就不用我截圖了吧,小夥伴都會吧。

項目運行正常,但須要加一個需求:用戶增長成功以後進行郵件發送通知。

目前有兩種解決方案

  • 直接在增長用戶方法中添加加發送郵件邏輯(相信不少小夥伴是這樣作的,見效快,還簡單);

    若是頻繁在增長用戶前或後添加新需求呢,還繼續加嗎,可能到最後增長用戶的方法變得很複雜,後期也很差維護;若是要去掉某一個功能,又得把代碼改回來,做爲程序員是否是又要和產品同事搞架啦(文明人,不動手);固然,若是需求固定,這種方式也不錯。

  • 面向切面實現,即在不影響原有代碼邏輯的狀況,動態的對方法進行攔截,在方法執行前或後添加業務便可。

項目中引入AOP(面向切面編程)思想
  • 原始動態代理實現;

    先來加個代理類,以下:

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Text;
    namespace Aop
    {
      // 繼承DispatchProxy
      public class MyProxy : DispatchProxy
      {
          //具體類型
          public object TargetClass { get; set; }
          protected override object Invoke(MethodInfo targetMethod, object[] args)
          {
              Console.WriteLine("增長用戶前執行業務");
              //調用原有方法
              targetMethod.Invoke(TargetClass, args);
              Console.WriteLine("增長用戶後執行業務");
              return true;
          }
      }
    }

    而後在Main函數直接使用便可,以下:

    class Program
    {
      static void Main(string[] args)
      {
          //原始需求
          User user = new User { Name = "Zoe", Age = 18 };
          IUserService userService = new UserService();
          userService.AddUser(user);
          Console.WriteLine("========動態代理 實現新需求=========");
          //1. 建立代理對象
          IUserService userService1 = DispatchProxy.Create<IUserService, MyProxy>();
          //2. 由於調用的是實例方法,須要傳提具體類型
          ((MyProxy)userService1).TargetClass = new UserService();
          userService1.AddUser(user);
          Console.ReadLine();
      }
    }

    動態代理就實現需求功能啦,能夠在用戶增長前或後都進行相關需求處理,運行看效果:

    圖片

  • 第三方庫Castle.Core封裝的美滋滋;

    經過上面演示,原生的動態代理實現面向切面編程顯得相對麻煩,好比強制轉換、傳遞類型等操做;經常使用的Castle.Core將動態代理進一步封裝,使用就相對方便點啦;此次定義一個攔截器便可:

    using Castle.DynamicProxy;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Aop
    {
      // 自定義攔截器
      public class MyIntercept : IInterceptor
      {
          public void Intercept(IInvocation invocation)
          {
              //執行原有方法以前
              Console.WriteLine("增長用戶前執行業務");
              //執行原有方法
              invocation.Proceed();
              //執行原有方法以後
              Console.WriteLine("增長用戶後執行業務");
          }
      }
    }

    Main函數中使用攔截器便可,以下:

    using AopModel;
    using AopService;
    using Castle.DynamicProxy;
    using System;
    using System.Reflection;
    using System.Reflection.Metadata;
    
    namespace Aop
    {
      class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("========原始需求=========");
              User user = new User { Name = "Zoe", Age = 18 };
              IUserService userService = new UserService();
              // 模擬增長一個用戶
              userService.AddUser(user);
              Console.WriteLine("========動態代理 實現新需求=========");
              //1. 建立代理對象
              IUserService userService1 = DispatchProxy.Create<IUserService, MyProxy>();
              //2. 由於調用的是實例方法,須要傳提具體類型
              ((MyProxy)userService1).TargetClass = new UserService();
              userService1.AddUser(user);
              Console.WriteLine("=============Castle.Core方式==============");
              //先實例化一個代理類生成器
              ProxyGenerator generator = new ProxyGenerator();
              //經過代理類生成器建立
              var u = generator.CreateInterfaceProxyWithTarget<IUserService>(new UserService(), new MyIntercept());
              u.AddUser(user);
              Console.ReadLine();
          }
      }
    }

    運行效果以下:

    圖片

  • Autofac集成了Castle.Core用着也挺不錯;

    Autofac已經集成了Castle.Core啦,在聊MemoryCache的時候就已經用到,使用比較簡單,能夠經過特性標註的方式就能夠針對某個類或接口的方法進行攔截增強,詳情請參考這篇文章(因MemoryCache鬧了個笑話)。

3. 應用場景

AOP思想是很優秀,但總不能到處都得用吧,需根據業務來評估是否須要;經常使用應用場景大概有如下幾個:

  • 安全控制:一般在Web開發的時候,會使用過濾器或攔截器進行權限驗證,這也是AOP思想的落地;對於客戶端程序,經過上述演示的幾種方式能夠輕鬆實現權限的統一管理和驗證;

  • 事務處理:相信小夥伴都寫過數據庫事務代碼吧,常規作法就是在業務方法中直接開啓事務,執行完成,提交或回滾便可,AOP思想也能很好處理這種狀況;

  • 異常處理:統一的異常處理是最好的選擇,除非是特殊的業務;一般Web有異常過濾器,客戶端程序能夠用上述幾種方式;

  • 日誌記錄:目前來講日誌記錄應該是做爲系統功能的一部分,AOP統一記錄是不錯的選擇;

  • 性能統計:以AOP的思想對方法進行先後監控,從而能夠分析其執行性能;

  • 緩存處理:緩存處理,如上次說到MemoryCache,加上AOP攔截應用,系統效率提高不錯哦

  • 業務輔變主不變:主業務變,但會不定時增長輔助需求的場景,好比增長用戶,後續可能在用戶新增成功以後會增長郵件通知、推送新用戶信息等功能。

源碼地址:https://github.com/zyq025/DotNetCoreStudyDemo

總結

先暫時聊這麼多吧,瞌睡啦,小夥伴們晚安嘍!!!

一個被程序搞醜的帥小夥,關注"Code綜藝圈",跟我一塊兒學~~~

相關文章
相關標籤/搜索