SpringAOP中的動(dòng)態(tài)代理技術(shù)深入解析
動(dòng)態(tài)代理
Spring的aop實(shí)現(xiàn)主要應(yīng)用了JDK動(dòng)態(tài)代理和Cglib動(dòng)態(tài)代理 這2種代理。
org.springframework.aop.framework.JdkDynamicAopProxy org.springframework.aop.framework.CglibAopProxy
spring默認(rèn)使用JDK動(dòng)態(tài)代理實(shí)現(xiàn)AOP,類如果實(shí)現(xiàn)了接口,spring就會(huì)用JDK動(dòng)態(tài)代理實(shí)現(xiàn)AOP. 如果目標(biāo)類沒(méi)有實(shí)現(xiàn)接口,spring則使用Cglib動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP.
- jdk動(dòng)態(tài)代理的優(yōu)勢(shì):jdk自身支持,減少依賴,可隨jdk平滑升級(jí),代碼實(shí)現(xiàn)簡(jiǎn)單
- cglib動(dòng)態(tài)代理的優(yōu)勢(shì):無(wú)需實(shí)現(xiàn)接口,達(dá)到無(wú)侵入,只操作我們關(guān)系的類,而不必為其他相關(guān)類增加工作量
spring事務(wù)便是基于spring aop實(shí)現(xiàn)的, spring事務(wù)的一些失效場(chǎng)景,如
1.使用了private方法,導(dǎo)致事務(wù)失效(被動(dòng)態(tài)代理的方法必須是public)
2.使用了final方法,導(dǎo)致事務(wù)失效(如果方法定義為final,JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理無(wú)法重寫(xiě)該方法)
3.同一類內(nèi)部方法調(diào)用:直接使用this對(duì)象調(diào)用方法,無(wú)法生成代理方法,導(dǎo)致事務(wù)失效
測(cè)試使用Cglib動(dòng)態(tài)代理
定義被代理的方法
package cn.demo.cglib; public class OrderService { /** * 被動(dòng)態(tài)代理的方法應(yīng)該是public公共的 * @param orderNo */ public void order(String orderNo){ System.out.println("order something..."); } }
定義cglib動(dòng)態(tài)代理時(shí)觸發(fā)的攔截器
package cn.demo.cglib; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 動(dòng)態(tài)代理類,實(shí)現(xiàn)方法攔截器MethodInterceptor接口 */ public class LogInterceptor implements MethodInterceptor { //cglib動(dòng)態(tài)代理,基于ASM機(jī)制實(shí)現(xiàn),通過(guò)生成目標(biāo)類的子類作為代理類 //使用cglib中的Enhancer來(lái)生成代理對(duì)象子類, public Object getProxyInstance(Class targetClass){ //1.工具類 Enhancer enhancer = new Enhancer(); //2.設(shè)置父類 enhancer.setSuperclass(targetClass); //3.設(shè)置回調(diào)函數(shù) enhancer.setCallback(this); //4.創(chuàng)建子類(代理對(duì)象) return enhancer.create(); } //并實(shí)現(xiàn)MethodInterceptor的intercept方法來(lái)實(shí)現(xiàn)增強(qiáng)功能 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("LogInterceptor: Before invoke---"); Object obj = methodProxy.invokeSuper(o,objects); System.out.println("LogInterceptor: After invoke---"); return obj; } }
使用cglib動(dòng)態(tài)代理出的實(shí)例
package cn.demo.cglib; public class CglibTest { public static void main(String[] args) { //Cglib動(dòng)態(tài)代理特點(diǎn) //1.需要引入cglib依賴jar (一般spring的核心包已經(jīng)有了cglib依賴) //2.被代理的類不需要接口信息,Cglib動(dòng)態(tài)代理可以攔截并包裝被代理類的所有方法 //3.代理類要實(shí)現(xiàn)MethodInterceptor接口 //4.代理類使用cglib中的Enhancer來(lái)生成目標(biāo)對(duì)象子類 //5.目標(biāo)類不能為final,因?yàn)閒inal類不能創(chuàng)建子類 OrderService orderService = (OrderService) new LogInterceptor() .getProxyInstance(OrderService.class); orderService.order("123"); } }
測(cè)試使用jdk動(dòng)態(tài)代理
定義被代理的方法及其接口
package cn.demo.proxy; public interface UserService { void login(String username, String password); }
package cn.demo.proxy; //jdk動(dòng)態(tài)代理的致命缺點(diǎn)就是目標(biāo)類必須實(shí)現(xiàn)某個(gè)接口 public class UserServiceImpl implements UserService{ @Override public void login(String username, String password) { System.out.println("User Login Service!"); } }
定義jdk動(dòng)態(tài)代理時(shí)觸發(fā)的攔截器
package cn.demo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; //通過(guò)實(shí)現(xiàn)InvocationHandler接口完成代理邏輯 public class LogHandler implements InvocationHandler { /** * 被代理的對(duì)象,實(shí)際的方法執(zhí)行者 */ Object target; public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在函數(shù)invoke之前之后,可以進(jìn)行一些自定義操作 //如 日志、事務(wù)、攔截器、權(quán)限控制等 System.out.println("Before Login ---"); Object result = method.invoke(target,args); System.out.println("After Login ---"); return result; } }
使用jdk動(dòng)態(tài)代理出的實(shí)例
package cn.demo.proxy; import java.lang.reflect.Proxy; public class JdkProxyTest { public static void main(String[] args) { //創(chuàng)建被代理的對(duì)象, UserService接口的實(shí)現(xiàn)類 UserServiceImpl userService = new UserServiceImpl(); //創(chuàng)建代理對(duì)象,包含3個(gè)參數(shù) ClassLoader 、 目標(biāo)類實(shí)現(xiàn)接口數(shù)組、 事件處理器 UserService userProxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new LogHandler(userService) ); //用代理出的實(shí)例調(diào)用login方法 userProxy.login("admin", "123456"); }
到此這篇關(guān)于SpringAOP中的動(dòng)態(tài)代理技術(shù)深入解析的文章就介紹到這了,更多相關(guān)SpringAOP動(dòng)態(tài)代理技術(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jdk環(huán)境變量配置切換jdk版本及安裝jdk后環(huán)境變量不生效問(wèn)題解決辦法
這篇文章主要介紹了jdk環(huán)境變量配置切換jdk版本及安裝jdk后環(huán)境變量不生效問(wèn)題解決辦法,包括配置JAVA_HOME、Path和CLASSPATH,以及如何驗(yàn)證配置是否成功,文章還講解了如何切換JDK版本,并解決了安裝新JDK后環(huán)境變量配置不生效的問(wèn)題,需要的朋友可以參考下2024-12-12SpringBoot Filter修改返回內(nèi)容,解決請(qǐng)求卡死200的錯(cuò)誤
這篇文章主要介紹了SpringBoot Filter修改返回內(nèi)容,解決請(qǐng)求卡死200的錯(cuò)誤問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07JAVA中l(wèi)ist,set,數(shù)組之間的轉(zhuǎn)換詳解
以下是對(duì)JAVA中l(wèi)ist,set,數(shù)組之間的轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下2013-09-09SpringCloud中分析講解Feign組件添加請(qǐng)求頭有哪些坑梳理
在spring?cloud的項(xiàng)目中用到了feign組件,簡(jiǎn)單配置過(guò)后即可完成請(qǐng)求的調(diào)用。又因?yàn)橛邢蛘?qǐng)求添加Header頭的需求,查閱了官方示例后,就覺(jué)得很簡(jiǎn)單,然后一頓操作之后調(diào)試報(bào)錯(cuò)...下面我們來(lái)詳細(xì)了解2022-06-06在mybatis執(zhí)行SQL語(yǔ)句之前進(jìn)行攔擊處理實(shí)例
本篇文章主要介紹了在mybatis執(zhí)行SQL語(yǔ)句之前進(jìn)行攔擊處理實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04Java實(shí)現(xiàn)度分秒坐標(biāo)轉(zhuǎn)十進(jìn)制度
隨著技術(shù)的發(fā)展,十進(jìn)制度因其精確性和便捷性在現(xiàn)代應(yīng)用中越來(lái)越受到青睞,下面我們就來(lái)看看如何使用Java實(shí)現(xiàn)度分秒坐標(biāo)轉(zhuǎn)十進(jìn)制度吧2024-12-12使用IDEA如何導(dǎo)入SpringBoot項(xiàng)目
這篇文章主要介紹了使用IDEA如何導(dǎo)入SpringBoot項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,2023-12-12