Spring之AOP兩種代理機(jī)制對(duì)比分析(JDK和CGLib動(dòng)態(tài)代理)
Spring AOP兩種代理機(jī)制對(duì)比
Spirng的AOP的動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制有兩種,分別是:
JDK動(dòng)態(tài)代理
具體實(shí)現(xiàn)原理:
1、通過實(shí)現(xiàn)InvocationHandlet接口創(chuàng)建自己的調(diào)用處理器
2、通過為Proxy類指定ClassLoader對(duì)象和一組interface來創(chuàng)建動(dòng)態(tài)代理
3、通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型就是調(diào)用處理器接口類型
4、通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)參入
JDK動(dòng)態(tài)代理是面向接口的代理模式,如果被代理目標(biāo)沒有接口那么Spring也無能為力,
Spring通過java的反射機(jī)制生產(chǎn)被代理接口的新的匿名實(shí)現(xiàn)類,重寫了其中AOP的增強(qiáng)方法。
CGLib動(dòng)態(tài)代理
CGLib是一個(gè)強(qiáng)大、高性能的Code生產(chǎn)類庫,可以實(shí)現(xiàn)運(yùn)行期動(dòng)態(tài)擴(kuò)展java類,Spring在運(yùn)行期間通過 CGlib繼承要被動(dòng)態(tài)代理的類,重寫父類的方法,實(shí)現(xiàn)AOP面向切面編程呢。
兩者對(duì)比:
JDK動(dòng)態(tài)代理是面向接口,在創(chuàng)建代理實(shí)現(xiàn)類時(shí)比CGLib要快,創(chuàng)建代理速度快。
CGLib動(dòng)態(tài)代理是通過字節(jié)碼底層繼承要代理類來實(shí)現(xiàn)(如果被代理類被final關(guān)鍵字所修飾,那么抱歉會(huì)失?。?,在創(chuàng)建代理這一塊沒有JDK動(dòng)態(tài)代理快,但是運(yùn)行速度比JDK動(dòng)態(tài)代理要快。
使用注意:
如果要被代理的對(duì)象是個(gè)實(shí)現(xiàn)類,那么Spring會(huì)使用JDK動(dòng)態(tài)代理來完成操作(Spirng默認(rèn)采用JDK動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制)
如果要被代理的對(duì)象不是個(gè)實(shí)現(xiàn)類那么,Spring會(huì)強(qiáng)制使用CGLib來實(shí)現(xiàn)動(dòng)態(tài)代理。
那么如何選擇的使用代理機(jī)制了?

通過配置Spring的中<aop:config>標(biāo)簽來顯示的指定使用動(dòng)態(tài)代理機(jī)制 proxy-target-class=true表示使用CGLib代理,如果為false就是默認(rèn)使用JDK動(dòng)態(tài)代理
SpringAOP兩種代理原理
SpringAOP代理
spingAOP代理有兩種:
- JDK動(dòng)態(tài)代理:目標(biāo)類必須實(shí)現(xiàn)一個(gè)接口
- CGLIB代理:目標(biāo)類必須繼承一個(gè)類
JDK動(dòng)態(tài)代理
JDK為什么一定要目標(biāo)類實(shí)現(xiàn)一個(gè)接口呢,這其實(shí)就得看看JDK動(dòng)態(tài)代理的原理了,其實(shí)JDK動(dòng)態(tài)代理它是先生成一個(gè)代理類然后他也是實(shí)現(xiàn)了目標(biāo)類實(shí)現(xiàn)的接口里面的方法,只是他還是調(diào)用的是目標(biāo)類的方法。
下面我們來自定義實(shí)現(xiàn)一下
//創(chuàng)建一個(gè)接口
public interface StudentBiz {
int add(String name);
void update(String name);
void find (String name);
}//創(chuàng)建一個(gè)類實(shí)現(xiàn)那個(gè)接口
public class StudentBizimpl implements StudentBiz{
@Override
public int add(String name) {
System.out.println("調(diào)用了studentBizimpl中的add"+name);
return 100;
}
@Override
public void update(String name) {
System.out.println("調(diào)用了studentBizimpl中的update"+name);
}
@Override
public void find(String name) {
System.out.println("調(diào)用了studentBizimpl中的find"+name);
}
}//jdk動(dòng)態(tài)代理三大重點(diǎn)
// 1.有目標(biāo)類的引用
// 2.有一個(gè)創(chuàng)建代理實(shí)例的方法createProxy()里面有Proxy.newProxyInstance()方法來創(chuàng)建代理實(shí)例
// 3.有一個(gè)回調(diào)方法invoke
public class LogAspect implements InvocationHandler {
private Object target;//目標(biāo)類的對(duì)象
public LogAspect(Object target){
this.target=target;
}
public Object createProxy(){
//新建一個(gè)代理實(shí)例
// 第一個(gè)參數(shù)是類加載器 第二個(gè)是得獲取到目標(biāo)類實(shí)現(xiàn)的接口 第三個(gè)是代理類對(duì)象
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
}
@Override//回調(diào)方法 當(dāng)jvm調(diào)用代理對(duì)象的被代理的方法時(shí),會(huì)由jvm自動(dòng)調(diào)用這個(gè)invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理類對(duì)象:"+proxy.getClass());
System.out.println("目標(biāo)的方法:"+method);
System.out.println("方法的參數(shù):"+args);
log();//這里就可以加增強(qiáng) 具體哪些方法加得看切入點(diǎn)表達(dá)式來判斷
Object o=method.invoke(this.target,args);//激活目標(biāo)類方法
return o;
}
private void log(){
System.out.println("前置增強(qiáng)");
}
}下面我們?cè)賮碜鲆粋€(gè)測(cè)試類:
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
StudentBiz sbm=new StudentBizimpl();
LogAspect la=new LogAspect(sbm);//放目標(biāo)類對(duì)象
Object o=la.createProxy();//創(chuàng)建一個(gè)代理類
if (o instanceof StudentBiz){
StudentBiz sb=(StudentBiz)o;
sb.add("張三");//這里就會(huì)自動(dòng)回調(diào)invoke方法
}
}
}得到以下結(jié)果:

CGLIB代理
CGLib是一個(gè)強(qiáng)大、高性能的Code生產(chǎn)類庫,可以實(shí)現(xiàn)運(yùn)行期動(dòng)態(tài)擴(kuò)展java類,Spring在運(yùn)行期間通過 CGlib繼承要被動(dòng)態(tài)代理的類,重寫父類的方法,實(shí)現(xiàn)AOP面向切面編程呢。
CGLIB代理其實(shí)也就是生成一個(gè)代理對(duì)象他也繼承了目標(biāo)類的父類中的方法,再通過回調(diào)自身引用目標(biāo)類的方法完成代理.
下面來簡(jiǎn)單地自定義實(shí)現(xiàn)一下
//做一個(gè)cglib代理類
public class LogAspectcglib implements MethodInterceptor {
private Object target;
public LogAspectcglib(Object target){
this.target=target;
}
public Object createProxy(){
Enhancer enhancer=new Enhancer();//用于生成代理對(duì)象
enhancer.setSuperclass(this.target.getClass());//設(shè)置父類
enhancer.setCallback(this);//設(shè)置回調(diào)用對(duì)象為本身
return enhancer.create();//創(chuàng)建代理對(duì)象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理類對(duì)象"+o.getClass());
System.out.println("目標(biāo)類的方法"+method);
System.out.println("目標(biāo)方法參數(shù)"+objects);
System.out.println("要代理的方法"+methodProxy);
Object returnvalue= method.invoke(this.target,objects);
return returnvalue;
}
}測(cè)試類:
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
StudentBizimpl sbm=new StudentBizimpl();
LogAspectcglib lac=new LogAspectcglib(sbm);
Object o=lac.createProxy();
if (o instanceof StudentBizimpl){
StudentBizimpl sb=(StudentBizimpl) o;
sb.add("張三");
}
}
}得到結(jié)果:

兩者對(duì)比
- JDK動(dòng)態(tài)代理是面向接口,在創(chuàng)建代理實(shí)現(xiàn)類時(shí)比CGLib要快,創(chuàng)建代理速度快。
- CGLib動(dòng)態(tài)代理是通過字節(jié)碼底層繼承要代理類來實(shí)現(xiàn)(如果被代理類被final關(guān)鍵字所修飾,那么抱歉會(huì)失敗),在創(chuàng)建代理這一塊沒有JDK動(dòng)態(tài)代理快,但是運(yùn)行速度比JDK動(dòng)態(tài)代理要快。
使用注意
如果要被代理的對(duì)象是個(gè)實(shí)現(xiàn)類,那么Spring會(huì)使用JDK動(dòng)態(tài)代理來完成操作(Spirng默認(rèn)采用JDK動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制)
如果要被代理的對(duì)象不是個(gè)實(shí)現(xiàn)類那么,Spring會(huì)強(qiáng)制使用CGLib來實(shí)現(xiàn)動(dòng)態(tài)代理。
如果要強(qiáng)制使用CGLIB代理則需在xml中配置如下:
<aop:aspectj-autoproxy proxy-target-class="true"/>
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 深入了解SpringAOP中的jdk動(dòng)態(tài)代理與CGlib
- Java兩種動(dòng)態(tài)代理JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理詳解
- Java的動(dòng)態(tài)代理模式之JDK代理詳解
- JDK動(dòng)態(tài)代理提高代碼可維護(hù)性和復(fù)用性利器
- Java JDK與cglib動(dòng)態(tài)代理有什么區(qū)別
- 解讀jdk動(dòng)態(tài)代理為什么必須實(shí)現(xiàn)接口
- Java實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理詳解
- Java反射(JDK)與動(dòng)態(tài)代理(CGLIB)詳解
- Java JDK動(dòng)態(tài)代理在攔截器和聲明式接口中的應(yīng)用小結(jié)
相關(guān)文章
淺談MyBatisPlus中LocalDateTime引發(fā)的一些問題和解決辦法
MyBatisPlus進(jìn)行數(shù)據(jù)庫操作時(shí),我們經(jīng)常會(huì)遇到處理日期時(shí)間類型的需求,本文主要介紹了淺談MyBatisPlus中LocalDateTime引發(fā)的一些問題和解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
Eclipse開發(fā)JavaWeb項(xiàng)目配置Tomcat的方法步驟
本文主要介紹了Eclipse開發(fā)JavaWeb項(xiàng)目配置Tomcat的方法步驟,首先介紹eclipse開發(fā)JavaWeb項(xiàng)目需要配置的相關(guān)環(huán)境,使用tomcat軟件在本地搭建服務(wù)器,然后再在eclipse環(huán)境下配置tomcat,感興趣的可以了解一下2021-08-08
Java利用Strategy模式實(shí)現(xiàn)堆排序
策略設(shè)計(jì)模式(Strategy):可以整體的替換一個(gè)算法的實(shí)現(xiàn)部分,能夠整體的替換算法,能讓我們輕松地用不同方法解決同一個(gè)問題。本文將利用Strategy模式實(shí)現(xiàn)堆排序,感興趣的可以學(xué)習(xí)一下2022-09-09
SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法
這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法,文中通過代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-04-04
SpringBoot后端接收數(shù)組對(duì)象的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot后端接收數(shù)組對(duì)象的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
SpringBoot實(shí)現(xiàn)啟動(dòng)項(xiàng)目后立即執(zhí)行的方法總結(jié)
在項(xiàng)目開發(fā)中某些場(chǎng)景必須要用到啟動(dòng)項(xiàng)目后立即執(zhí)行方式的功能,所以這篇文章就來和大家聊聊實(shí)現(xiàn)立即執(zhí)行的幾種方法,希望對(duì)大家有所幫助2023-05-05
Java實(shí)現(xiàn)爬取往期所有雙色球開獎(jiǎng)結(jié)果功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)爬取往期所有雙色球開獎(jiǎng)結(jié)果功能,涉及Java網(wǎng)頁抓取、正則替換、文件讀寫等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
java線程之使用Runnable接口創(chuàng)建線程的方法
本篇文章介紹了,java中使用Runnable接口創(chuàng)建線程的方法。需要的朋友參考下2013-05-05

