image
一盹兢、AOP的基本概念:
1瘦赫、什么是aop:
AOP(Aspect Oriented Programming)稱為面向切面編程,在程序開發(fā)中主要用來解決一些系統(tǒng)層面上的問題蛤迎,比如日志,事務(wù)含友,權(quán)限等待替裆,Struts2的攔截器設(shè)計就是基于AOP的思想,是個比較經(jīng)典的例子窘问。
在不改變原有的邏輯的基礎(chǔ)上辆童,增加一些額外的功能。代理也是這個功能惠赫,讀寫分離也能用aop來做把鉴。
AOP可以說是OOP(Object Oriented Programming,面向?qū)ο缶幊蹋┑难a充和完善。OOP引入封裝庭砍、繼承场晶、多態(tài)等概念來建立一種對象層次結(jié)構(gòu),用于模擬公共行為的一個集合怠缸。不過OOP允許開發(fā)者定義縱向的關(guān)系诗轻,但并不適合定義橫向的關(guān)系,例如日志功能揭北。日志代碼往往橫向地散布在所有對象層次中扳炬,而與它對應(yīng)的對象的核心功能毫無關(guān)系對于其他類型的代碼,如安全性搔体、異常處理和透明的持續(xù)性也都是如此恨樟,這種散布在各處的無關(guān)的代碼被稱為橫切(cross cutting),在OOP設(shè)計中疚俱,它導(dǎo)致了大量代碼的重復(fù)劝术,而不利于各個模塊的重用。
AOP技術(shù)恰恰相反计螺,它利用一種稱為"橫切"的技術(shù)夯尽,剖解開封裝的對象內(nèi)部,并將那些影響了多個類的公共行為封裝到一個可重用模塊登馒,并將其命名為"Aspect"匙握,即切面。所謂"切面"陈轿,簡單說就是那些與業(yè)務(wù)無關(guān)圈纺,卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來,便于減少系統(tǒng)的重復(fù)代碼麦射,降低模塊之間的耦合度蛾娶,并有利于未來的可操作性和可維護性。
使用"橫切"技術(shù)潜秋,AOP把軟件系統(tǒng)分為兩個部分:核心關(guān)注點和橫切關(guān)注點蛔琅。業(yè)務(wù)處理的主要流程是核心關(guān)注點,與之關(guān)系不大的部分是橫切關(guān)注點峻呛。橫切關(guān)注點的一個特點是罗售,他們經(jīng)常發(fā)生在核心關(guān)注點的多處,而各處基本相似钩述,比如權(quán)限認證寨躁、日志、事物牙勘。AOP的作用在于分離系統(tǒng)中的各種關(guān)注點职恳,將核心關(guān)注點和橫切關(guān)注點分離開來。
2、AOP的相關(guān)概念:
(1)橫切關(guān)注點:對哪些方法進行攔截放钦,攔截后怎么處理色徘,這些關(guān)注點稱之為橫切關(guān)注點
(2)Aspect(切面):通常是一個類,里面可以定義切入點和通知
(3)JointPoint(連接點):程序執(zhí)行過程中明確的點最筒,一般是方法的調(diào)用贺氓。被攔截到的點,因為Spring只支持方法類型的連接點床蜘,所以在Spring中連接點指的就是被攔截到的方法辙培,實際上連接點還可以是字段或者構(gòu)造器
(4)Advice(通知):AOP在特定的切入點上執(zhí)行的增強處理,有before(前置),after(后置),afterReturning(最終),afterThrowing(異常),around(環(huán)繞)
(5)Pointcut(切入點):就是帶有通知的連接點邢锯,在程序中主要體現(xiàn)為書寫切入點表達式
(6)weave(織入):將切面應(yīng)用到目標對象并導(dǎo)致代理對象創(chuàng)建的過程
(7)introduction(引入):在不修改代碼的前提下扬蕊,引入可以在運行期為類動態(tài)地添加一些方法或字段
(8)AOP代理(AOP Proxy):AOP框架創(chuàng)建的對象,代理就是目標對象的加強丹擎。Spring中的AOP代理可以使JDK動態(tài)代理尾抑,也可以是CGLIB代理,前者基于接口蒂培,后者基于子類
(9)目標對象(Target Object): 包含連接點的對象再愈。也被稱作被通知或被代理對象。POJO
3护戳、Advice通知類型介紹:
(1)Before:在目標方法被調(diào)用之前做增強處理,@Before只需要指定切入點表達式即可
(2)AfterReturning:在目標方法正常完成后做增強,@AfterReturning除了指定切入點表達式后翎冲,還可以指定一個返回值形參名returning,代表目標方法的返回值
(3)AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點表達式后,還可以指定一個throwing的返回值形參名,可以通過該形參名
來訪問目標方法中所拋出的異常對象
(4)After:在目標方法完成之后做增強媳荒,無論目標方法時候成功完成抗悍。@After可以指定一個切入點表達式
(5)Around:環(huán)繞通知,在目標方法完成前后做增強處理,環(huán)繞通知是最重要的通知類型,像事務(wù),日志等都是環(huán)繞通知,注意編程中核心是一個ProceedingJoinPoint
4、AOP使用場景:
Authentication 權(quán)限
Caching 緩存
Context passing 內(nèi)容傳遞
Error handling 錯誤處理
Lazy loading 懶加載
Debugging 調(diào)試
logging, tracing, profiling and monitoring 記錄跟蹤 優(yōu)化 校準
Performance optimization 性能優(yōu)化
Persistence 持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務(wù)
二钳枕、你們認為的AOP 應(yīng)該是這樣的
對沒錯 缴渊,這是AOP 最常用的方式,作為日志處理鱼炒、異常處理衔沼、參數(shù)校驗 。
三昔瞧、我要說的AOP 長這樣
先描述下業(yè)務(wù)場景:對于 業(yè)務(wù)類型的訂單接口:我們可以通過數(shù)據(jù)校驗避免指蚁,訂單數(shù)據(jù)重復(fù)提交(例如:開始、結(jié)束硬爆、拒絕訂單) 但是對于 新建訂單 是無法通過 校驗實現(xiàn)的。 為了避免重復(fù)數(shù)據(jù)擎鸠,最好的就是引入鎖機制處理缀磕。但問題來了,又不是所有的接口都需要鎖,只是對特定的接口需要添加鎖袜蚕。添加鎖肯定就要try糟把、catch、finally每次寫太費勁了牲剃,況且一旦修改鎖遣疯,更換其他類型的鎖很麻煩,工作量也很大凿傅。所以缠犀,AOP ,嗯 聪舒,靠譜辨液。 可以在指定方法上加自定義注解,讓AOP 掃描帶有自定義注解的方法就ok了箱残。
細心的同學(xué)會發(fā)現(xiàn)滔迈,紅框內(nèi)容有點不太一樣 @annotation。execution(* com.x.x.x.x.contxroller..*.*(..))" 指的是過濾方法(里面是 [正則表表達式](https://blog.csdn.net/corbin_zhang/article/details/80576809) )被辑。@annotation() 里面放的是注解的全類名燎悍,驚喜不?
/**
*該注解 是在方法上 添加鎖的
*/
@Target({java.lang.annotation.ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLock {
? ? String desc() default "";
}
/**
* 元注解(meta-annotation):
*
* 元注解的作用就是負責(zé)注解其他注解盼理。Java5.0定義了4個標準的meta-annotation類型谈山,它們被用來提供對其它 annotation類型作說明。Java5.0定義的元注解:
* 1.@Target,
* 2.@Retention,
* 3.@Documented,
* 4.@Inherited
*/
/**
* @Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages榜揖、types(類勾哩、接口、枚舉举哟、Annotation類型)思劳、類型成員(方法、構(gòu)造方法妨猩、成員變量潜叛、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量壶硅、catch參數(shù))威兜。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
*
* 作用:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
*
* 取值(ElementType)有:
*
* 1.CONSTRUCTOR:用于描述構(gòu)造器
* 2.FIELD:用于描述域
* 3.LOCAL_VARIABLE:用于描述局部變量
* 4.METHOD:用于描述方法
* 5.PACKAGE:用于描述包
* 6.PARAMETER:用于描述參數(shù)
* 7.TYPE:用于描述類庐椒、接口(包括注解類型) 或enum聲明
*/
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
/**
* @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現(xiàn)在源代碼中椒舵,而被編譯器丟棄;而另一些卻被編譯在class文件中约谈;編譯在class文件中的Annotation可能會被虛擬機忽略笔宿,而另一些在class被裝載時將被讀壤缰印(請注意并不影響class的執(zhí)行,因為Annotation與class在使用上是被分離的)泼橘。使用這個meta-Annotation可以對 Annotation的“生命周期”限制涝动。
*
* 作用:表示需要在什么級別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)
*
* 取值(RetentionPoicy)有:
*
* 1.SOURCE:在源文件中有效(即源文件保留)
* 2.CLASS:在class文件中有效(即class保留)
* 3.RUNTIME:在運行時有效(即運行時保留)
*
* Retention meta-annotation類型有唯一的value作為成員炬灭,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值醋粟。
*/
@Retention(RetentionPolicy.RUNTIME)
/**
* @Documented用于描述其它類型的annotation應(yīng)該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化重归。
* Documented是一個標記注解米愿,沒有成員。
*/
@Documented
/**
* @Inherited 元注解是一個標記注解提前,@Inherited闡述了某個被標注的類型是被繼承的吗货。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類狈网。
*
* 注意:@Inherited annotation類型是被標注過的class的子類所繼承宙搬。類并不從它所實現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation拓哺。
*
* 當(dāng)@Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME勇垛,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時士鸥,反射代碼檢查將展開工作:檢查class和其父類闲孤,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn),或者到達類繼承結(jié)構(gòu)的頂層烤礁。
*/
@Inherited
代碼如下:
/**
* 鎖注解 切面
*/
@Component //作為一個bean 被spring 掃描到
@Aspect
@Order(100)
1. //order越小越是最先執(zhí)行讼积,但更重要的是最先執(zhí)行的最后結(jié)束。order默認值是2147483647
public class MethodLockAspect {
? ? @Around("@annotation(com.x.x.x.x.annoxtation.MethodLock)")
? ? public Object around(ProceedingJoinPoint joinPoint) {
? ? ? ? return MethodLockAspectUtils.methodLock(joinPoint);
? ? }
}
* @Apsect:將當(dāng)前類標識為一個切面脚仔;
* @Pointcut:定義切點勤众,這里使用的是條件表達式;
* @Before:前置增強鲤脏,就是在目標方法執(zhí)行之前執(zhí)行们颜;
* @AfterReturning:后置增強,方法退出時執(zhí)行猎醇;
* @AfterThrowing:有異常時該方法執(zhí)行窥突;
* @After:最終增強,無論什么情況都會執(zhí)行硫嘶;
* @Afround:環(huán)繞增強阻问;
//切面中 鎖具體的實現(xiàn)
public class MethodLockAspectUtils {
? ? /**
? ? * 互斥鎖 參數(shù)默認false,不公平鎖
? ? */
? ? private static Lock lock = new ReentrantLock(false);
? ? public static Object methodLock(ProceedingJoinPoint joinPoint){
? ? ? ? lock.lock();
? ? ? ? Object obj = null;
? ? ? ? try {
? ? ? ? ? ? obj = joinPoint.proceed();
? ? ? ? } catch (Throwable e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? lock.unlock();
? ? ? ? }
? ? ? ? return obj;
? ? }
}
四沦疾、使用:
在需要添加同步方法上使用注解即可
五称近、注意
這里只是簡單的是鎖機制贡蓖,后期進行更新切換,zookeeper煌茬、redis,mysql等分布式鎖彻桃。