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