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

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

CGLIB代理
CGLib是一個強大、高性能的Code生產(chǎn)類庫,可以實現(xiàn)運行期動態(tài)擴展java類,Spring在運行期間通過 CGlib繼承要被動態(tài)代理的類,重寫父類的方法,實現(xiàn)AOP面向切面編程呢。
CGLIB代理其實也就是生成一個代理對象他也繼承了目標類的父類中的方法,再通過回調(diào)自身引用目標類的方法完成代理.
下面來簡單地自定義實現(xiàn)一下
//做一個cglib代理類
public class LogAspectcglib implements MethodInterceptor {
private Object target;
public LogAspectcglib(Object target){
this.target=target;
}
public Object createProxy(){
Enhancer enhancer=new Enhancer();//用于生成代理對象
enhancer.setSuperclass(this.target.getClass());//設(shè)置父類
enhancer.setCallback(this);//設(shè)置回調(diào)用對象為本身
return enhancer.create();//創(chuàng)建代理對象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理類對象"+o.getClass());
System.out.println("目標類的方法"+method);
System.out.println("目標方法參數(shù)"+objects);
System.out.println("要代理的方法"+methodProxy);
Object returnvalue= method.invoke(this.target,objects);
return returnvalue;
}
}測試類:
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é)果:

兩者對比
- JDK動態(tài)代理是面向接口,在創(chuàng)建代理實現(xiàn)類時比CGLib要快,創(chuàng)建代理速度快。
- CGLib動態(tài)代理是通過字節(jié)碼底層繼承要代理類來實現(xiàn)(如果被代理類被final關(guān)鍵字所修飾,那么抱歉會失?。?,在創(chuàng)建代理這一塊沒有JDK動態(tài)代理快,但是運行速度比JDK動態(tài)代理要快。
使用注意
如果要被代理的對象是個實現(xiàn)類,那么Spring會使用JDK動態(tài)代理來完成操作(Spirng默認采用JDK動態(tài)代理實現(xiàn)機制)
如果要被代理的對象不是個實現(xiàn)類那么,Spring會強制使用CGLib來實現(xiàn)動態(tài)代理。
如果要強制使用CGLIB代理則需在xml中配置如下:
<aop:aspectj-autoproxy proxy-target-class="true"/>
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
java導出數(shù)據(jù)庫中Excel表格數(shù)據(jù)的方法
這篇文章主要為大家詳細介紹了java導出數(shù)據(jù)庫中Excel表格數(shù)據(jù)的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Java如何自定義類數(shù)組的創(chuàng)建和初始化
這篇文章主要介紹了Java如何自定義類數(shù)組的創(chuàng)建和初始化,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringBoot下載Excel文件時,報錯文件損壞的解決方案
這篇文章主要介紹了SpringBoot下載Excel文件時,報錯文件損壞的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
logback StatusListener的定義方法源碼解讀
這篇文章主要為大家介紹了logback StatusListener的定義方法源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
Java案例使用比較排序器comparator實現(xiàn)成績排序
這篇文章主要介紹了Java案例使用比較排序器comparator實現(xiàn)成績排序,主要通過案例用TreeSet集合存儲多個學生信息,并遍歷該集合,要按照總分從高到低進行排序,下文介紹需要的朋友可以參考一下2022-04-04
Spring MVC如何使用@RequestParam注解獲取參數(shù)
這篇文章主要介紹了Spring MVC實現(xiàn)使用@RequestParam注解獲取參數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10

