日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

再说 Spring AOP

發(fā)布時(shí)間:2025/3/21 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 再说 Spring AOP 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是 AOP

AOP(Aspect-OrientedProgramming,面向方面編程),可以說是 OOP(Object-Oriented Programing,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。OOP 引入封裝、繼承和多態(tài)性等概念來建立一種對(duì)象層次結(jié)構(gòu),用以模擬公共行為的一個(gè)集合。當(dāng)我們需要為分散的對(duì)象引入公共行為的時(shí)候,OOP 則顯得無能為力。也就是說,OOP 允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無關(guān)系。對(duì)于其他類型的代碼,如安全性、異常處理和透明的持續(xù)性也是如此。這種散布在各處的無關(guān)的代碼被稱為橫切(cross-cutting)代碼,在 OOP 設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。

AOP 實(shí)現(xiàn)的關(guān)鍵在于 AOP 框架自動(dòng)創(chuàng)建的 AOP 代理,AOP 代理主要分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理的代表為 AspectJ;而動(dòng)態(tài)代理則以 Spring AOP 為代表。靜態(tài)代理是編譯期實(shí)現(xiàn),動(dòng)態(tài)代理是運(yùn)行期實(shí)現(xiàn),可想而知前者擁有更好的性能。

靜態(tài)代理是編譯階段生成 AOP 代理類,也就是說生成的字節(jié)碼就織入了增強(qiáng)后的 AOP 對(duì)象;動(dòng)態(tài)代理則不會(huì)修改字節(jié)碼,而是在內(nèi)存中臨時(shí)生成一個(gè) AOP 對(duì)象,這個(gè) AOP 對(duì)象包含了目標(biāo)對(duì)象的全部方法,并且在特定的切點(diǎn)做了增強(qiáng)處理,并回調(diào)原對(duì)象的方法。

Spring AOP 中的動(dòng)態(tài)代理主要有兩種方式,JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理。JDK 動(dòng)態(tài)代理通過反射來接收被代理的類,并且要求被代理的類必須實(shí)現(xiàn)一個(gè)接口。JDK 動(dòng)態(tài)代理的核心是 InvocationHandler 接口和 Proxy 類。

如果目標(biāo)類沒有實(shí)現(xiàn)接口,那么 Spring AOP 會(huì)選擇使用 CGLIB 來動(dòng)態(tài)代理目標(biāo)類。CGLIB(Code Generation Library),是一個(gè)代碼生成的類庫,可以在運(yùn)行時(shí)動(dòng)態(tài)的生成某個(gè)類的子類,注意,CGLIB 是通過繼承的方式做的動(dòng)態(tài)代理,因此如果某個(gè)類被標(biāo)記為 final,那么它是無法使用 CGLIB 做動(dòng)態(tài)代理的,諸如 private 的方法也是不可以作為切面的。

我們分別通過實(shí)例來研究 AOP 的具體實(shí)現(xiàn)。

直接使用 Spring AOP

首先需要引入相關(guān)依賴,我這里是用了 SpringBoot 的相關(guān) starter

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>

然后定義需要切入的接口和實(shí)現(xiàn)。為了簡單起見,定義一個(gè)接口Speakable和一個(gè)具體的實(shí)現(xiàn)類PersonSpring,只有兩個(gè)方法sayHi()和sayBye()。

public interface Speakable {void sayHi();void sayBye(); } @Service public class PersonSpring implements Speakable {@Overridepublic void sayHi() {try {Thread.currentThread().sleep(30);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Hi!!");}@Overridepublic void sayBye() {try {Thread.currentThread().sleep(30);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Bye!!");}}

現(xiàn)在我們需要實(shí)現(xiàn)一個(gè)功能,記錄sayHi()和sayBye()的執(zhí)行時(shí)間。
我們定義了一個(gè)MethodMonitor類用來記錄 Method 執(zhí)行時(shí)間

public class MethodMonitor {private long start;private String methodName;public MethodMonitor(String methodName) {this.methodName = methodName;this.start = System.currentTimeMillis();System.out.println(this.methodName + "monitor begin...");}public void log() {long elapsedTime = System.currentTimeMillis() - start;System.out.println("Method:" + this.methodName + ", elapsedTime:" + elapsedTime + "millis");System.out.println(this.methodName + "monitor end.");}}

光有這個(gè)類還是不夠的,希望有個(gè)靜態(tài)方法用起來更順手,像這樣

MonitorSession.start(methodName); doWork(); MonitorSession.end();

說干就干,定義一個(gè)MonitorSession

public class MonitorSession {private static ThreadLocal<MethodMonitor> threadLocal = new ThreadLocal<>();public static void start(String methodName) {threadLocal.set(new MethodMonitor(methodName));}public static void end() {threadLocal.get().log();}}

準(zhǔn)備工作都做完了, 接下來只需要我們做好切面的編碼

@Aspect @Component public class MonitorAdvice {@Pointcut("execution (* com.windmt.springaop.service.Speakable.*(..))")public void pointcut() {}@Around("pointcut()")public void around(ProceedingJoinPoint pjp) throws Throwable {MonitorSession.start(pjp.getSignature().getName());pjp.proceed();MonitorSession.end();}}

如何使用?寫一個(gè)啟動(dòng)函數(shù)吧。

@SpringBootApplication public class SpringAopApplication {@Autowiredprivate Speakable personSpring;public static void main(String[] args) {SpringApplication.run(SpringAopApplication.class, args);}@Beanpublic CommandLineRunner commandLineRunner(ApplicationContext ctx) {return args -> {System.out.println("********** spring aop **********");personSpring.sayHi();personSpring.sayBye();System.exit(0);};}}

運(yùn)行后輸出:

********** spring aop ********** sayHi monitor begin... Hi!! Method:sayHi, elapsedTime:48 millis sayHi monitor end. sayBye monitor begin... Bye!! Method:sayBye, elapsedTime:34 millis sayBye monitor end.

JDK 動(dòng)態(tài)代理

剛剛的例子其實(shí)內(nèi)部實(shí)現(xiàn)機(jī)制就是 JDK 動(dòng)態(tài)代理,因?yàn)镻ersonSpring實(shí)現(xiàn)了一個(gè)接口。
為了不和第一個(gè)例子沖突,我們?cè)俣x一個(gè)PersonIndie來實(shí)現(xiàn)Speakable,實(shí)現(xiàn)和之前的完全一樣,只是注意這個(gè)實(shí)現(xiàn)是不帶 Spring Annotation 的,所以他不會(huì)被 Spring 托管。

public class PersonIndie implements Speakable {@Overridepublic void sayHi() {try {Thread.currentThread().sleep(30);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Hi!!");}@Overridepublic void sayBye() {try {Thread.currentThread().sleep(30);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Bye!!");}}

重頭戲來了,我們需要利用InvocationHandler實(shí)現(xiàn)一個(gè)代理,讓它去包含Person這個(gè)對(duì)象。那么在運(yùn)行時(shí)實(shí)際上執(zhí)行的是這個(gè)代理的方法,然后代理再去執(zhí)行真正的方法。所以我們得以在執(zhí)行真正方法的前后做一些手腳。JDK 動(dòng)態(tài)代理是利用反射實(shí)現(xiàn),直接看代碼。

public class DynamicProxy implements InvocationHandler {private Object target;public DynamicProxy(Object obj) {this.target = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MonitorSession.start(method.getName());Object obj = method.invoke(this.target, args);MonitorSession.end();return obj;}public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}}

通過getProxy可以得到這個(gè)代理對(duì)象,invoke就是具體的執(zhí)行方法,可以看到我們?cè)趫?zhí)行每個(gè)真正的方法前后都加了 Monitor。

再來一個(gè)工廠類來獲取 Person 代理對(duì)象

public class PersonProxyFactory {public static Speakable newJdkProxy() {DynamicProxy proxy = new DynamicProxy(new PersonIndie());return proxy.getProxy();}}

具體使用

@SpringBootApplication public class SpringAopApplication {public static void main(String[] args) {SpringApplication.run(SpringAopApplication.class, args);}@Beanpublic CommandLineRunner commandLineRunner(ApplicationContext ctx) {return args -> {System.out.println("********** spring jdk proxy **********");Speakable person = PersonProxyFactory.newJdkProxy();person.sayHi();person.sayBye();System.exit(0);};}}

運(yùn)行并輸出:

********** spring jdk proxy ********** sayHi monitor begin... Hi!! Method:sayHi, elapsedTime:35 millis sayHi monitor end. sayBye monitor begin... Bye!! Method:sayBye, elapsedTime:32 millis sayBye monitor end.

CGLib 動(dòng)態(tài)代理

我們?cè)傩陆ㄒ粋€(gè)Person來,這次不實(shí)現(xiàn)任何接口。

public class Person {public void sayHi() {try {Thread.currentThread().sleep(30);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Hi!!");}public void sayBye() {try {Thread.currentThread().sleep(30);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Bye!!");}}

如果 Spring 識(shí)別到所代理的類沒有實(shí)現(xiàn) Interface,那么就會(huì)使用 CGLib 來創(chuàng)建動(dòng)態(tài)代理,原理實(shí)際上成為所代理類的子類。

public class CGLibProxy implements MethodInterceptor {private static volatile CGLibProxy instance;private CGLibProxy() {}public static CGLibProxy getInstance() {if (instance == null) {synchronized (CGLibProxy.class) {if (instance == null) {instance = new CGLibProxy();}}}return instance;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {MonitorSession.start(method.getName());Object obj = methodProxy.invokeSuper(o, objects);MonitorSession.end();return obj;}private Enhancer enhancer = new Enhancer();public <T> T getProxy(Class<T> clazz) {enhancer.setSuperclass(clazz);enhancer.setCallback(this);return (T) enhancer.create();}}

類似的通過getProxy可以得到這個(gè)代理對(duì)象,intercep就是具體的執(zhí)行方法,可以看到我們?cè)趫?zhí)行每個(gè)真正的方法前后都加了 Monitor。
在工廠類中增加獲得 Person 代理類的方法

public static Person newCGLibProxy() {return CGLibProxy.getInstance().getProxy(Person.class); }

具體使用

Person person = PersonProxyFactory.newCGLibProxy(); person.sayHi(); person.sayBye();

輸出結(jié)果

********** CGLib proxy ********** sayHi monitor begin... Hi!! Method:sayHi, elapsedTime:38 millis sayHi monitor end. sayBye monitor begin... Bye!! Method:sayBye, elapsedTime:35 millis sayBye monitor end.

以上 code 都可以通過?Github?中獲取。

  • 本文作者:?Yibo
  • 本文鏈接:?https://windmt.com/2018/04/01/spring-aop-words-again/
  • 版權(quán)聲明:?本博客所有文章除特別聲明外,均采用?CC BY-NC-SA 4.0?許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!

總結(jié)

以上是生活随笔為你收集整理的再说 Spring AOP的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。