AOP(面向切面编程)大概了解一下
前言
上一篇在聊MemoryCache的時(shí)候,用到了Autofac提供的攔截器進(jìn)行面向切面編程,很明顯能體會(huì)到其優(yōu)勢(shì),既然涉及到了,那就趁熱打鐵,一起來探探面向切面編程。
正文
1. 概述
在軟件業(yè),AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預(yù)編譯方式和運(yùn)行期間動(dòng)態(tài)代理實(shí)現(xiàn)程序功能統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP(面向?qū)ο蟪绦蛟O(shè)計(jì))的延續(xù),是軟件開發(fā)中的一個(gè)熱點(diǎn),是函數(shù)式編程的一種衍生范型。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
---來自百度百科
總結(jié)優(yōu)點(diǎn)
對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,業(yè)務(wù)之間耦合度降低;
提高程序的可重用性,同時(shí)程序更容易維護(hù);
提高開發(fā)效率,不用花大量的時(shí)間在業(yè)務(wù)中增加代碼,還能降低風(fēng)險(xiǎn);
其實(shí)AOP的本質(zhì)就是動(dòng)態(tài)代理,何為動(dòng)態(tài)代理呢?
動(dòng)態(tài)代理就是在程序運(yùn)行時(shí),創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,并對(duì)目標(biāo)對(duì)象中的方法進(jìn)行功能性增強(qiáng)的一種技術(shù)。通俗一點(diǎn)來說就是在運(yùn)行期間對(duì)方法的攔截,在方法執(zhí)行前后進(jìn)行額外的業(yè)務(wù)處理,從而在不嵌入原有代碼邏輯情況下就能增強(qiáng)被攔截方法的業(yè)務(wù)能力。
理論先到這,一起來看看用代碼怎么實(shí)現(xiàn)吧?
2. 實(shí)踐檢驗(yàn)真理(到底優(yōu)不優(yōu)秀)
先來一個(gè)控制臺(tái)項(xiàng)目,什么都沒有,從頭開始擼代碼,先來看看項(xiàng)目結(jié)構(gòu):
老案例了,還是假裝在進(jìn)行用戶維護(hù),模擬對(duì)用戶進(jìn)行增刪改查。這次就直接上代碼啦啊,根據(jù)項(xiàng)目結(jié)構(gòu)依次看看代碼:
在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();//?模擬增加一個(gè)用戶userService.AddUser(user);Console.ReadLine();} }
這樣項(xiàng)目就正常運(yùn)行啦,這個(gè)就不用我截圖了吧,小伙伴都會(huì)吧。
項(xiàng)目運(yùn)行正常,但需要加一個(gè)需求:用戶增加成功之后進(jìn)行郵件發(fā)送通知。
目前有兩種解決方案
直接在增加用戶方法中添加加發(fā)送郵件邏輯(相信很多小伙伴是這樣做的,見效快,還簡(jiǎn)單);
如果頻繁在增加用戶前或后添加新需求呢,還繼續(xù)加嗎,可能到最后增加用戶的方法變得很復(fù)雜,后期也不好維護(hù);如果要去掉某一個(gè)功能,又得把代碼改回來,作為程序員是不是又要和產(chǎn)品同事搞架啦(文明人,不動(dòng)手);當(dāng)然,如果需求固定,這種方式也不錯(cuò)。
面向切面實(shí)現(xiàn),即在不影響原有代碼邏輯的情況,動(dòng)態(tài)的對(duì)方法進(jìn)行攔截,在方法執(zhí)行前或后添加業(yè)務(wù)即可。
項(xiàng)目中引入AOP(面向切面編程)思想
原始動(dòng)態(tài)代理實(shí)現(xiàn);
先來加個(gè)代理類,如下:
using?System; using?System.Collections.Generic; using?System.Reflection; using?System.Text; namespace?Aop {//?繼承DispatchProxypublic?class?MyProxy?:?DispatchProxy{//具體類型public?object?TargetClass?{?get;?set;?}protected?override?object?Invoke(MethodInfo?targetMethod,?object[]?args){Console.WriteLine("增加用戶前執(zhí)行業(yè)務(wù)");//調(diào)用原有方法targetMethod.Invoke(TargetClass,?args);Console.WriteLine("增加用戶后執(zhí)行業(yè)務(wù)");return?true;}} }然后在Main函數(shù)直接使用即可,如下:
class?Program {static?void?Main(string[]?args){//原始需求User?user?=?new?User?{?Name?=?"Zoe",?Age?=?18?};IUserService?userService?=?new?UserService();userService.AddUser(user);Console.WriteLine("========動(dòng)態(tài)代理?實(shí)現(xiàn)新需求=========");//1.?創(chuàng)建代理對(duì)象IUserService?userService1?=?DispatchProxy.Create<IUserService,?MyProxy>();//2.?因?yàn)檎{(diào)用的是實(shí)例方法,需要傳提具體類型((MyProxy)userService1).TargetClass?=?new?UserService();userService1.AddUser(user);Console.ReadLine();} }動(dòng)態(tài)代理就實(shí)現(xiàn)需求功能啦,可以在用戶增加前或后都進(jìn)行相關(guān)需求處理,運(yùn)行看效果:
第三方庫(kù)Castle.Core封裝的美滋滋;
通過上面演示,原生的動(dòng)態(tài)代理實(shí)現(xiàn)面向切面編程顯得相對(duì)麻煩,比如強(qiáng)制轉(zhuǎn)換、傳遞類型等操作;常用的Castle.Core將動(dòng)態(tài)代理進(jìn)一步封裝,使用就相對(duì)方便點(diǎn)啦;這次定義一個(gè)攔截器即可:
using?Castle.DynamicProxy; using?System; using?System.Collections.Generic; using?System.Text;namespace?Aop {//?自定義攔截器public?class?MyIntercept?:?IInterceptor{public?void?Intercept(IInvocation?invocation){//執(zhí)行原有方法之前Console.WriteLine("增加用戶前執(zhí)行業(yè)務(wù)");//執(zhí)行原有方法invocation.Proceed();//執(zhí)行原有方法之后Console.WriteLine("增加用戶后執(zhí)行業(yè)務(wù)");}} }Main函數(shù)中使用攔截器即可,如下:
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();//?模擬增加一個(gè)用戶userService.AddUser(user);Console.WriteLine("========動(dòng)態(tài)代理?實(shí)現(xiàn)新需求=========");//1.?創(chuàng)建代理對(duì)象IUserService?userService1?=?DispatchProxy.Create<IUserService,?MyProxy>();//2.?因?yàn)檎{(diào)用的是實(shí)例方法,需要傳提具體類型((MyProxy)userService1).TargetClass?=?new?UserService();userService1.AddUser(user);Console.WriteLine("=============Castle.Core方式==============");//先實(shí)例化一個(gè)代理類生成器ProxyGenerator?generator?=?new?ProxyGenerator();//通過代理類生成器創(chuàng)建var?u?=?generator.CreateInterfaceProxyWithTarget<IUserService>(new?UserService(),?new?MyIntercept());u.AddUser(user);Console.ReadLine();}} }運(yùn)行效果如下:
Autofac集成了Castle.Core用著也挺不錯(cuò);
Autofac已經(jīng)集成了Castle.Core啦,在聊MemoryCache的時(shí)候就已經(jīng)用到,使用比較簡(jiǎn)單,可以通過特性標(biāo)注的方式就可以針對(duì)某個(gè)類或接口的方法進(jìn)行攔截加強(qiáng),詳情請(qǐng)參考這篇文章(因MemoryCache鬧了個(gè)笑話)。
3. 應(yīng)用場(chǎng)景
AOP思想是很優(yōu)秀,但總不能處處都得用吧,需根據(jù)業(yè)務(wù)來評(píng)估是否需要;常用應(yīng)用場(chǎng)景大概有以下幾個(gè):
安全控制:通常在Web開發(fā)的時(shí)候,會(huì)使用過濾器或攔截器進(jìn)行權(quán)限驗(yàn)證,這也是AOP思想的落地;對(duì)于客戶端程序,通過上述演示的幾種方式可以輕松實(shí)現(xiàn)權(quán)限的統(tǒng)一管理和驗(yàn)證;
事務(wù)處理:相信小伙伴都寫過數(shù)據(jù)庫(kù)事務(wù)代碼吧,常規(guī)做法就是在業(yè)務(wù)方法中直接開啟事務(wù),執(zhí)行完成,提交或回滾即可,AOP思想也能很好處理這種情況;
異常處理:統(tǒng)一的異常處理是最好的選擇,除非是特殊的業(yè)務(wù);通常Web有異常過濾器,客戶端程序可以用上述幾種方式;
日志記錄:目前來說日志記錄應(yīng)該是作為系統(tǒng)功能的一部分,AOP統(tǒng)一記錄是不錯(cuò)的選擇;
性能統(tǒng)計(jì):以AOP的思想對(duì)方法進(jìn)行前后監(jiān)控,從而可以分析其執(zhí)行性能;
緩存處理:緩存處理,如上次說到MemoryCache,加上AOP攔截應(yīng)用,系統(tǒng)效率提升不錯(cuò)哦
業(yè)務(wù)輔變主不變:主業(yè)務(wù)變,但會(huì)不定時(shí)增加輔助需求的場(chǎng)景,比如增加用戶,后續(xù)可能在用戶新增成功之后會(huì)增加郵件通知、推送新用戶信息等功能。
源碼地址:https://github.com/zyq025/DotNetCoreStudyDemo
總結(jié)
先暫時(shí)聊這么多吧,瞌睡啦,小伙伴們晚安嘍!!!
一個(gè)被程序搞丑的帥小伙,關(guān)注"Code綜藝圈",跟我一起學(xué)~~~
圖片總結(jié)
以上是生活随笔為你收集整理的AOP(面向切面编程)大概了解一下的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BeetleX使用bootstrap5开
- 下一篇: 如何选择 WebClient Http