![Java實(shí)現(xiàn)AOP的幾種方式_第1頁(yè)](http://file2.renrendoc.com/fileroot_temp3/2021-7/18/166e9cd9-307d-41d5-85a8-bc3d26cd3dda/166e9cd9-307d-41d5-85a8-bc3d26cd3dda1.gif)
![Java實(shí)現(xiàn)AOP的幾種方式_第2頁(yè)](http://file2.renrendoc.com/fileroot_temp3/2021-7/18/166e9cd9-307d-41d5-85a8-bc3d26cd3dda/166e9cd9-307d-41d5-85a8-bc3d26cd3dda2.gif)
![Java實(shí)現(xiàn)AOP的幾種方式_第3頁(yè)](http://file2.renrendoc.com/fileroot_temp3/2021-7/18/166e9cd9-307d-41d5-85a8-bc3d26cd3dda/166e9cd9-307d-41d5-85a8-bc3d26cd3dda3.gif)
![Java實(shí)現(xiàn)AOP的幾種方式_第4頁(yè)](http://file2.renrendoc.com/fileroot_temp3/2021-7/18/166e9cd9-307d-41d5-85a8-bc3d26cd3dda/166e9cd9-307d-41d5-85a8-bc3d26cd3dda4.gif)
![Java實(shí)現(xiàn)AOP的幾種方式_第5頁(yè)](http://file2.renrendoc.com/fileroot_temp3/2021-7/18/166e9cd9-307d-41d5-85a8-bc3d26cd3dda/166e9cd9-307d-41d5-85a8-bc3d26cd3dda5.gif)
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Java 實(shí)現(xiàn) AOP 的幾種方式Spring AOP 實(shí)現(xiàn)機(jī)制標(biāo)簽: javaspringaop2014-05-23 11:43 12359人閱讀 評(píng)論 (1) 收藏 舉報(bào) 分類(lèi): JAVA 技術(shù)( 34 ) 技術(shù)類(lèi)( 11) (1) AOP 的各種實(shí)現(xiàn)在編譯器修改源代碼、 在運(yùn)行期字節(jié)碼加載 前修改字節(jié)碼或字節(jié)碼加載后動(dòng)態(tài)創(chuàng)建代理類(lèi)的字節(jié)碼。以 下是各種實(shí)現(xiàn)機(jī)制的比較: 類(lèi)別分為靜態(tài) AOP (包括靜態(tài) 織入)和動(dòng)態(tài) AOP (包括動(dòng)態(tài)代理、動(dòng)態(tài)字節(jié)碼生成、自定 義類(lèi)加載器、字節(jié)碼轉(zhuǎn)換)。靜態(tài)織入:a、原理:在編譯期, 切面直接以字節(jié)碼形式編譯到目標(biāo)字節(jié)碼文件中;b 、優(yōu)點(diǎn):對(duì)系統(tǒng)性能無(wú)影
2、響;c、缺點(diǎn):不夠靈活; 動(dòng)態(tài)代理:a、原理:在運(yùn)行期,目標(biāo)類(lèi)加載后,為接口動(dòng)態(tài)生成代理類(lèi)。 將切面織入到代理類(lèi)中;b、優(yōu)點(diǎn):更靈活;c、缺點(diǎn):切入 的關(guān)注點(diǎn)要實(shí)現(xiàn)接口;動(dòng)態(tài)字節(jié)碼生成:a、原理:在運(yùn)行期,目標(biāo)類(lèi)加載后, 動(dòng)態(tài)構(gòu)建字節(jié)碼文件生成目標(biāo)類(lèi)的子類(lèi), 將切面邏輯加入到子類(lèi)中;b、優(yōu)點(diǎn):沒(méi)有接口也可以織入; c、缺點(diǎn):擴(kuò)展類(lèi)的實(shí)例方法為 final時(shí),無(wú)法進(jìn)行織入;自定義類(lèi)加載器a、原理:在運(yùn)行期,目標(biāo)加載前,將切面邏 輯加到目標(biāo)字節(jié)碼里;b、優(yōu)點(diǎn):可以對(duì)絕大部分類(lèi)進(jìn)行織 入;c、缺點(diǎn):代碼中若使用了其它類(lèi)加載器,則這些類(lèi)將 不會(huì)被織入; 字節(jié)碼轉(zhuǎn)換a、原理:在運(yùn)行期,所有類(lèi)加載器加載字
3、節(jié)碼前進(jìn)行攔截; b 、優(yōu)點(diǎn):可以對(duì)所有類(lèi)進(jìn)行織 入;c、缺點(diǎn): (2) Joinpoint :攔截點(diǎn),如某個(gè)業(yè)務(wù)方法; Pointcut : Jointpoint 的表達(dá)式,表示攔截哪些方法。一個(gè) Pointcut 對(duì)應(yīng)多個(gè) Joinpoint ; Advice :要切入的邏輯。 Before Advice :在方法前切入; After Advice :在方法后 切入,拋出異常時(shí)也會(huì)切入; After Returning Advice :在 方法返回后切入,拋出異常不會(huì)切入;After ThrowingAdvice :在方法拋出異常時(shí)切入; Around Advice :在方 法執(zhí)行前后切入
4、,可以中斷或忽略原有流程的執(zhí)行;目標(biāo)切面織入器代理類(lèi) JointpointAdvicePointcutPointcut 織入器通過(guò)在切面中定義 pointcut 來(lái)搜索目標(biāo)(被代理類(lèi))的 Jointpoint (切入點(diǎn)),然后把要切入的邏輯( advice )織入 到目標(biāo)對(duì)象里,生成代理類(lèi)。( 3)動(dòng)態(tài)代理的實(shí)現(xiàn) Java在 JDK1.3 后引入的動(dòng)態(tài)代理機(jī)制,使我們可以在運(yùn)行期動(dòng) 態(tài)的創(chuàng)建代理類(lèi)。使用動(dòng)態(tài)代理實(shí)現(xiàn) AOP 需要四個(gè)角色: 被代理的類(lèi)、被代理類(lèi)的接口、織入器( Proxy.newProxyInstance() )、 InvocationHandler 。織入器 使用接口反射機(jī)制
5、生成一個(gè)代理類(lèi),然后在這個(gè)代理類(lèi)中織 入代碼(切入邏輯)。InvocationHandler 是切面,包含了 Advice 和 Pointcut 。 動(dòng)態(tài)代理在運(yùn)行期通過(guò)接口動(dòng)態(tài)生成代理類(lèi)。使用反射大量生成類(lèi)文件可能引起Full GC 造成性能影響,因?yàn)樽止?jié)碼文件加載后會(huì) 存放在 JVM 運(yùn)行時(shí)區(qū)的方法區(qū)中(或持久代)。當(dāng)方法區(qū)滿的時(shí)候, 會(huì)引起 Full GC 。 因此當(dāng)大量使用動(dòng)態(tài)代理時(shí),可以將持久代設(shè)置大一些,減 少 Full GC 次數(shù)。 動(dòng)態(tài)代理的核心其實(shí)就是代理對(duì)象的生成, 即 Proxy.newProxyInstance() 。其中 getProxyClass() 方法用 于獲取
6、代理類(lèi),主要做了三件事:在當(dāng)前類(lèi)加載器的緩存里 搜索是否有代理類(lèi),沒(méi)有則生成代理類(lèi)并緩存在本地 JVM 里??梢允褂?JD-GUI 反編譯軟件打開(kāi) jrelibrt.jar 。 動(dòng)態(tài)代 理生成的代理類(lèi),類(lèi)似于: public class ProxyBusiness implements IBusiness privateInvocationHandler h;publicProxyBusiness(InvocationHandler h) this.h = h; public void doSomeThing() tyrMethod m = (h.target).getClass().getM
7、ethod("doSomeThing", null); h.invoke(this, m , null); catch(Throwable e) /測(cè)試 public static void main(String args) LogInvocationHandler handler= new LogInvocationHandler(new Business();new ProxyBusiness(handler).doSomeThing(); 代理的目的是調(diào)用目標(biāo)方法時(shí)轉(zhuǎn)而執(zhí)行 InvocationHandler類(lèi)的 invoke 方法! ( 4)動(dòng)態(tài)字節(jié)碼生成使用動(dòng)態(tài)
8、字節(jié)碼 生成技術(shù)實(shí)現(xiàn) AOP 原理:在運(yùn)行期間目標(biāo)字節(jié)碼加載后, 生成目標(biāo)類(lèi)的子類(lèi),將切面邏輯加入到子類(lèi)中,所以使用 Cglib 實(shí)現(xiàn) AOP 不需要基于接口。 使用 Cglib 實(shí)現(xiàn)動(dòng)態(tài)字節(jié) 碼: Cglib 是高性能的 code 生成類(lèi)庫(kù), 可以在運(yùn)行期間擴(kuò)展 java 類(lèi)和實(shí)現(xiàn) java 接口。 它封裝了 Asm ,使用 Cglib 前要引 入 Asm 的 jar 。如: public static void byteCodeGe() /創(chuàng)建一個(gè)織入器 Enhancer enhancer = new Enhancer();/ 設(shè)置父類(lèi)enhancer.setSuperclass(Busi
9、ness.class);/ 設(shè)置需要織入的邏輯enhancer.setCallback(newLogIntercept();/ 使用織入器創(chuàng)建子類(lèi)IBusinessnewBusiness = (IBusiness)enhancer.create(); newBusiness.doSomeThing(); public class LogIntercept implements MethodInterceptor public Objectintercept(Object target, Method method, Object args, MethodProxy proxy) throws
10、Throwable/ 執(zhí)行原有邏輯,注意這里是invokeSuper Object rev =/執(zhí)行織proxy.invokeSuper(target, args); 入的日志 if(method.getName().equals("doSomeThing") 記錄日 志 "); returnrev; ( 5 )自定義類(lèi)加載器實(shí)現(xiàn)一個(gè)自定義類(lèi)加載 器,在類(lèi)加載到 JVM 之前直接修改類(lèi)的方法,并將切入邏 輯織入到這個(gè)方法里, 然后將修改后的字節(jié)碼文件交給 JVM 運(yùn)行。 Javassist 是一個(gè)編輯字節(jié)碼的框架,可以很簡(jiǎn)單操 作字節(jié)碼。它可以在運(yùn)行期定義或修改
11、Class 。使用 Javassist 實(shí)現(xiàn) AOP 的原理是在字節(jié)碼加載前直接修改需要切入的方 法,比使用 Cglib 實(shí)現(xiàn) AOP 更加高效。 原理:系統(tǒng)類(lèi)加載 器 > 啟動(dòng) > 自定義類(lèi)加載器(類(lèi)加載監(jiān)聽(tīng)器) > 載入 > 類(lèi)文件使用系統(tǒng)類(lèi)加載器啟動(dòng)自定義 的類(lèi)加載器,在這個(gè)類(lèi)加載器里加一個(gè)類(lèi)加載監(jiān)聽(tīng)器,監(jiān)聽(tīng) 器發(fā)現(xiàn)目標(biāo)類(lèi)被加載時(shí)就織入切入邏輯。 啟動(dòng)自定義的類(lèi) 加載器: /獲取存放 CtClass 的容器 ClassPoolClassPool cp = ClassPool.getDefault();/ 創(chuàng)建一個(gè)類(lèi)加載器 Loade
12、r cl = new Loader();/ 增加一個(gè)轉(zhuǎn)換器 cl.addTranslator(cp, new MyTranslator();/ 啟動(dòng) MyTranslator 的 main 函數(shù) cl.run("javassist.JavassistAopDemo$MyTranslator", args); 類(lèi)加載監(jiān)聽(tīng)器: public static class MyTranslator implementsTranslator public void start(ClassPoolpool) throws Exception /* 類(lèi)加載到 JVM 前進(jìn)行代碼織入 */p
13、ublic voidonLoad(ClassPool pool, String classname)if(!"model$Business".equals(classname) return ;/通過(guò)獲取類(lèi)文件tryCtClass cc =pool.get(classname); 獲得指定方法名的方法/CtMethod m = cc.getDeclaredMethod("doSomeThing"); /在方法執(zhí)行前插入代碼m.insertBefore("System.out.println("記錄日志");");
14、catch(Exception) public static void main(String args)Business b = new Business();b.doSomeThing(); CtClass 是一個(gè) class 文 件的抽象描述??梢允褂?insertAfter() 在方法的末尾插入代 碼,使用 insertAt() 在指定行插入代碼。 使用自定義的類(lèi)加載器實(shí)現(xiàn) AOP 在性能上要優(yōu)于動(dòng)態(tài)代理和 Cglib ,因?yàn)樗?會(huì)產(chǎn)生新類(lèi)。但存在一個(gè)問(wèn)題,就是若其他的類(lèi)加載器來(lái)加 載類(lèi)的話,這些類(lèi)將不會(huì)被攔截。(6 )字節(jié)碼轉(zhuǎn)換自定義的類(lèi)加載器實(shí)現(xiàn) AOP 只能攔截自己加載的字節(jié)碼
15、,有沒(méi) 有能夠監(jiān)控所有類(lèi)加載器加載字節(jié)碼? > 有,使用 Instrumentation ,它是 Java 5 提供的新特性。使用 Instrumentation 可以構(gòu)建一個(gè)字節(jié)碼轉(zhuǎn)換器, 在字節(jié)碼加載 前進(jìn)行轉(zhuǎn)換。使用 Instrumentation 和 javassist 實(shí)現(xiàn) AOP 。 一、構(gòu)建字節(jié)碼轉(zhuǎn)換器首先創(chuàng)建字節(jié)碼轉(zhuǎn)換器,該轉(zhuǎn)換器負(fù) 責(zé)攔截 Business 類(lèi),并在 Business 類(lèi)的 doSomeThing 方 法前使用 javassist 加入記錄日志的代碼。 public class MyClassFileTransformer implements
16、ClassFileTransformer /* 字節(jié)碼加載到 JVM前會(huì)進(jìn)入這個(gè)方法 */ public byte transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomainthrows/ 若加載 BusinessreturnprotectionDomain, byte classfileBuffer)Exception類(lèi)才攔截if(!"model/Business".equals(className)null; /j
17、avassist 的包名是用點(diǎn)分割的,要轉(zhuǎn)換下if(className.indexOf("/") != -1) className =className.replaceAll("/",try/通過(guò)包名獲取類(lèi)文件CtClass cc = ClassPool.getDefault().get(className); /獲得指定方法名的方法CtMethod m = cc.getDeclaredMethod("doSomeThing"); /在方法執(zhí)行前插入代碼 記錄日志 ");"); return cc.toByteco
18、de(); catch (Exception e) return null; 二、注冊(cè)轉(zhuǎn)換器使用 premain 函 數(shù)注冊(cè)字節(jié)碼轉(zhuǎn)換器, 該方法在 main 函數(shù)之前執(zhí)行。 public class MyClassFileTransformer implements ClassFileTransformer public static voidpremain(String options, Instrumentation ins) / 注冊(cè)自己的字節(jié)碼轉(zhuǎn)換器 三、配置ins.addTransformer(new MyClassFileTransformer();和執(zhí)行需要告訴 JVM 在啟動(dòng)
19、 main 函數(shù)之前,需要先執(zhí)行 premain 函數(shù)。首先需要將 premain 函數(shù)所在的類(lèi)打成 jar 包。并修改該 jar 包里的 META-INFMANIFEST.MF 文件。 Manifest-Version:1.0Premain-Class:bci. MyClassFileTransformer 然后在 JVM 的啟動(dòng)參數(shù)里加上: -javaagent:D:javaprojectsopencometProjectAoplibaop. jar 四、輸出執(zhí)行 main 函數(shù), 會(huì)發(fā)現(xiàn)切入的代碼無(wú)侵入性的 織入進(jìn)去了。 public static void main(String ar
20、gs) new Business().doSomeThing(); 2 、 PS ( 1 ) AOP 能做的事情: 性能監(jiān)控:在方法調(diào)用前后記錄調(diào)用時(shí)間, 方法執(zhí)行太長(zhǎng)或超時(shí)報(bào)警。 緩存代理: 緩存某方法的返回值, 下次執(zhí)行該方法時(shí), 直接從緩存里獲取。 軟件破解:使用 AOP 修改軟件的驗(yàn)證類(lèi)的判斷邏輯。工作流系統(tǒng):工作流系統(tǒng)需 要將業(yè)務(wù)代碼和流程引擎代碼混合在一起執(zhí)行,可以使用 AOP 將其分離, 并動(dòng)態(tài)掛接業(yè)務(wù)。 權(quán)限驗(yàn)證: 方法執(zhí)行前驗(yàn) 證是否有權(quán)限執(zhí)行當(dāng)前方法。 ( 2 )方法調(diào)用成功后 > 統(tǒng)計(jì)調(diào)用次數(shù) > 存入緩存服務(wù)器 > 每日存入數(shù)
21、據(jù)庫(kù)因?yàn)槊刻斓姆椒ㄕ{(diào)用次數(shù)近百萬(wàn),為了降低數(shù)據(jù)庫(kù)壓力 不能實(shí)時(shí)入庫(kù)。 一、如何使用:只要配置了注解的方法將 會(huì)被統(tǒng)計(jì)調(diào)用次數(shù)。MethodInvokeTimesMonitor(value="aaa", returnValue=false)public void aa() 二、如何配置:使用AspectJ 的方式配置 AOP 。需要啟動(dòng)對(duì) AspectJ 的支持。 <aop:aspectj-autoproxy proxy-target-class="true"/>true 表示讓 spring 使用 Cglib 實(shí)現(xiàn) AOP ,
22、配置為 false 表示使用動(dòng)態(tài)代理實(shí)現(xiàn) AOP ,默認(rèn) 使用動(dòng)態(tài)代理。 三、定義注解 Retention(RetentionPolicy.RUNTIME)Target(Element Type.METHOD)public interface MethodInvokeTimesMonitor String value(); boolean returnValue() default true; 四、定義切面,在切面 中定義攔截的方法和在方法返回后記錄調(diào)用次數(shù)的 Advice 。 在這里定義攔截所有配置了注解的方法。 Aspectpublic class MethodAspect /* 切入點(diǎn),
23、所有配置 MethodInvokeTimesMonitor 注解的方法 /* 統(tǒng)計(jì)方法的調(diào)用次數(shù) */AfterReturning(value="MethodAspect.allMethodInvoke TimesMonitor() &&annotation(methodInvokeTimesMonitor)", returning="retVal")public void statInvokeTimes(MethodInvokeTimesMonitor methodInvokeTimesMonitor, Object r
24、etVal)String name = methodInvokeTimesMonitor.value(); boolean returnValue =methodInvokeTimesMonitor.returnValue(); 其中Pointcut 用于定義切入點(diǎn)表達(dá)式。 AfterReturning 表示 在方法執(zhí)行后進(jìn)行切入,里面的MethodAspect.allMethodInvokeTimesMonitor() 表示使用這 個(gè)方法的切入點(diǎn)表達(dá)式, annotation(methodInvokeTimesMonitor) 表示將當(dāng)參數(shù)傳 遞給 statInvokeTimes() 方法, returning="retVal" 表示將被
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度年度現(xiàn)房買(mǎi)賣(mài)合同書(shū)附房屋產(chǎn)權(quán)過(guò)戶指南
- 二零二五年度門(mén)窗廠家與經(jīng)銷(xiāo)商市場(chǎng)風(fēng)險(xiǎn)評(píng)估與應(yīng)對(duì)協(xié)議
- 二零二五年度勞動(dòng)合同解除與經(jīng)濟(jì)補(bǔ)償協(xié)議范本
- 二零二五年度泳池水質(zhì)不合格免責(zé)合同
- 2025年度社會(huì)保險(xiǎn)基金投資風(fēng)險(xiǎn)控制協(xié)議
- 2025年度貨車(chē)司機(jī)勞動(dòng)合同與貨運(yùn)車(chē)輛維修保養(yǎng)服務(wù)協(xié)議
- 2025年度土地使用權(quán)贈(zèng)與贈(zèng)予協(xié)議書(shū)
- 湘教版數(shù)學(xué)八年級(jí)上冊(cè)3.2《立方根》聽(tīng)評(píng)課記錄2
- 2025年美司那合作協(xié)議書(shū)
- 湘教版數(shù)學(xué)九年級(jí)下冊(cè)《3.1 投影》聽(tīng)評(píng)課記錄4
- GB/T 44823-2024綠色礦山評(píng)價(jià)通則
- 人教版英語(yǔ)高考試卷與參考答案(2024年)
- 河砂、碎石生產(chǎn)質(zhì)量保證措施方案
- 三位數(shù)除以兩位數(shù)過(guò)關(guān)練習(xí)口算題大全附答案
- 紅樓夢(mèng)服飾文化
- 湖北省2024年村干部定向考試真題
- 2024年沙石材料運(yùn)輸合同
- 浙江省中小學(xué)心理健康教育課程標(biāo)準(zhǔn)
- 老年人能力評(píng)估標(biāo)準(zhǔn)解讀-講義課件
- 醫(yī)保物價(jià)管理培訓(xùn)
- 《共情的力量》課件
評(píng)論
0/150
提交評(píng)論