Spring AOP里的靜態(tài)代理和動態(tài)代理用法詳解
什么是代理?
為某一個對象創(chuàng)建一個代理對象,程序不直接用原本的對象,而是由創(chuàng)建的代理對象來控制原對象,通過代理類這中間一層,能有效控制對委托類對象的直接訪問,也可以很好地隱藏和保護(hù)委托類對象,同時也為實施不同控制策略預(yù)留了空間
什么是靜態(tài)代理?
由程序創(chuàng)建或特定工具自動生成源代碼,在程序運行前,代理類的.class文件就已經(jīng)存在
通過將目標(biāo)類與代理類實現(xiàn)同一個接口,讓代理類持有真實類對象,然后在代理類方法中調(diào)用真實類方法,在調(diào)用真實類方法的前后添加我們所需要的功能擴展代碼來達(dá)到增強的目的。
優(yōu)點
- 代理使客戶端不需要知道實現(xiàn)類是什么,怎么做,而客戶端只需知道代理即可
- 方便增加功能,擴展業(yè)務(wù)邏輯
缺點
- 代理類中常出現(xiàn)大量冗余的代碼,非常不利于擴展和維護(hù)
- 如果接口增加一個方法,除了所有實現(xiàn)類需要實現(xiàn)這個方法外,所有代理類也需要實現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度
案例演示
PayService.java(接口)
package net.cybclass.sp.proxy; public interface PayService { /** * 支付回調(diào) * @param outTradeNo 訂單號 * @return */ String callback(String outTradeNo); /** * 下單 * @param userId 用戶id * @param productId 產(chǎn)品id * @return */ int save(int userId,int productId); }
PayServiceImpl.java(接口實現(xiàn)類)
package net.cybclass.sp.proxy; public class PayServiceImpl implements PayService{ public String callback(String outTradeNo) { System.out.println("目標(biāo)類 PayServiceImpl 回調(diào) 方法 callback"); return outTradeNo; } public int save(int userId, int productId) { System.out.println("目標(biāo)類 PayServiceImpl 回調(diào) 方法 save"); return productId; } }
StaticProxyPayServiceImpl.java(接口實現(xiàn)類,靜態(tài)代理)
package net.cybclass.sp.proxy; public class StaticProxyPayServiceImpl implements PayService{ private PayService payService; public StaticProxyPayServiceImpl(PayService payService) { this.payService=payService; } public String callback(String outTradeNo) { System.out.println("StaticProxyPayServiceImpl callback begin"); String result=payService.callback(outTradeNo); System.out.println("StaticProxyPayServiceImpl callback end"); return result; } public int save(int userId, int productId) { System.out.println("StaticProxyPayServiceImpl save begin"); int id = payService.save(userId, productId); System.out.println("StaticProxyPayServiceImpl save end"); return id; } }
演示
什么是動態(tài)代理?
在程序運行時,運用反射機制動態(tài)創(chuàng)建而成,無需手動編寫代碼
JDK動態(tài)代理
CGLIB動態(tài)代理(原理:是對指定的業(yè)務(wù)類生成一個子類,并覆蓋其中的業(yè)務(wù)方法來實現(xiàn)代理)
jdk動態(tài)代理演示
定義一個類,去實現(xiàn)InvocationHandler這個接口,并車從寫invoke方法 //Object proxy:被代理的對象 //Method method:要調(diào)用的方法 //Object[] args:方法調(diào)用時所需要參數(shù) public Object invoke(Object proxy, Method method, Object[] args){}
PayService.java(接口)
package net.cybclass.sp.proxy; public interface PayService { /** * 支付回調(diào) * @param outTradeNo 訂單號 * @return */ String callback(String outTradeNo); /** * 下單 * @param userId 用戶id * @param productId 產(chǎn)品id * @return */ int save(int userId,int productId); }
PayServiceImpl.java(接口實現(xiàn)類)
package net.cybclass.sp.proxy; public class PayServiceImpl implements PayService{ public String callback(String outTradeNo) { System.out.println("目標(biāo)類 PayServiceImpl 回調(diào) 方法 callback"); return outTradeNo; } public int save(int userId, int productId) { System.out.println("目標(biāo)類 PayServiceImpl 回調(diào) 方法 save"); return productId; } }
JDKProxy.java(jdk動態(tài)代理類)
package net.cybclass.sp.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxy implements InvocationHandler { //目標(biāo)類 private Object targetObject; /** * 獲取代理對象 * @param targetObject 目標(biāo)類 * @return */ public Object newProxyInstance(Object targetObject) { this.targetObject = targetObject; //綁定關(guān)系,也就是和具體的那個實現(xiàn)類關(guān)聯(lián) return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } /** * JDK動態(tài)代理 * * @param proxy 靜態(tài)代理對象 * @param method 要調(diào)用的方法 * @param args 方法調(diào)用時所需要參數(shù) * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { System.out.println("通過JDK動態(tài)代理調(diào)用"+method.getName()+",打印日志 begin"); result = method.invoke(targetObject, args); System.out.println("通過JDK動態(tài)代理調(diào)用"+method.getName()+",打印日志 end"); } catch (Exception ex) { ex.printStackTrace(); } return result; } }
CGLIB動態(tài)代理演示
PayService.java(接口)
package net.cybclass.sp.proxy; public interface PayService { /** * 支付回調(diào) * @param outTradeNo 訂單號 * @return */ String callback(String outTradeNo); /** * 下單 * @param userId 用戶id * @param productId 產(chǎn)品id * @return */ int save(int userId,int productId); }
PayServiceImpl.java(接口實現(xiàn)類)
package net.cybclass.sp.proxy; public class PayServiceImpl implements PayService{ public String callback(String outTradeNo) { System.out.println("目標(biāo)類 PayServiceImpl 回調(diào) 方法 callback"); return outTradeNo; } public int save(int userId, int productId) { System.out.println("目標(biāo)類 PayServiceImpl 回調(diào) 方法 save"); return productId; } }
CGLIBProxy.java(CGLIB動態(tài)代理類)
package net.cybclass.sp.proxy; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGLIBProxy implements MethodInterceptor { //目標(biāo)類 private Object targetObject; //綁定關(guān)系 public Object newProxyInstance(Object targetObject){ this.targetObject=targetObject; Enhancer enhancer=new Enhancer(); //設(shè)置代理類的父類(目標(biāo)類) enhancer.setSuperclass(this.targetObject.getClass()); //設(shè)置回調(diào)函數(shù) enhancer.setCallback(this); //創(chuàng)建子類(代理對象) return enhancer.create(); } public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result=null; try { System.out.println("通過CGLIB動態(tài)代理調(diào)用"+method.getName()+",打印日志 begin"); result=methodProxy.invokeSuper(o,args); System.out.println("通過CGLIB動態(tài)代理調(diào)用"+method.getName()+",打印日志 end"); } catch (Exception ex){ ex.printStackTrace(); } return result; } }
總結(jié)
動態(tài)代理與靜態(tài)代理相比較,最大的好處是接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個集中的方法中處理,解耦和易維護(hù)。
兩種動態(tài)代理的區(qū)別
- JDK動態(tài)代理:要求目標(biāo)對象實現(xiàn)一個接口,但是有時候目標(biāo)對象只是一個單獨的對象,并沒有實現(xiàn)任何的接口,這個時候就可以用CGLIB動態(tài)代理
- JDK動態(tài)代理是自帶的,CGLIB需要引入第三方包
- CGLIB動態(tài)代理,它是內(nèi)存中構(gòu)建一個子類對象從而實現(xiàn)對目標(biāo)對象功能的擴展
- CGLIB動態(tài)代理基于繼承來實現(xiàn)代理,所以無法對final類,private方法和static方法實現(xiàn)代理
Spring AOP中的代理使用的默認(rèn)策略
- 如果目標(biāo)對象實現(xiàn)類接口,則默認(rèn)采用JDK動態(tài)代理
- 如果目標(biāo)對象沒有實現(xiàn)接口,則采用CGLIB進(jìn)行動態(tài)代理
到此這篇關(guān)于 Spring AOP里的靜態(tài)代理和動態(tài)代理用法詳解的文章就介紹到這了,更多相關(guān) Spring AOP 靜態(tài)代理 動態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文搞懂Java常見的三種代理模式(靜態(tài)代理、動態(tài)代理和cglib代理)
Java中常見的三種代理模式是靜態(tài)代理模式、動態(tài)代理模式和CGLIB代理模式,本文就來給大家詳細(xì)的講解一下這三種代理模式,感興趣的小伙伴跟著小編一起來看看吧2023-08-08SpringBoot Actuator未授權(quán)訪問漏洞修復(fù)詳解
這篇文章主要介紹了SpringBoot Actuator未授權(quán)訪問漏洞修復(fù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08SpringBoot使用ExceptionHandler做異常處理
這篇文章主要介紹了SpringBoot使用ExceptionHandler做異常處理,這篇文章通過多種方法案例來介紹該項技術(shù)的使用,需要的朋友可以參考下2021-06-06Spring MVC之WebApplicationContext_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了Spring MVC之WebApplicationContext的相關(guān)資料,需要的朋友可以參考下2017-08-08