Spring中的兩種代理JDK和CGLIB的區(qū)別淺談
一、原理區(qū)別:
Java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類(lèi),在調(diào)用具體方法前調(diào)用InvokeHandler來(lái)處理。
而cglib動(dòng)態(tài)代理是利用asm開(kāi)源包,對(duì)代理對(duì)象類(lèi)的class文件加載進(jìn)來(lái),通過(guò)修改其字節(jié)碼生成子類(lèi)來(lái)處理。
1、如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP
2、如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP
3、如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)了接口,必須采用CGLIB庫(kù),spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換
如何強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP?
(1)添加CGLIB庫(kù),SPRING_HOME/cglib/.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別?
(1)JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理,而不能針對(duì)類(lèi)
(2)CGLIB是針對(duì)類(lèi)實(shí)現(xiàn)代理,主要是對(duì)指定的類(lèi)生成一個(gè)子類(lèi),覆蓋其中的方法
因?yàn)槭抢^承,所以該類(lèi)或方法最好不要聲明成final
二、代碼實(shí)現(xiàn)
package com.fy.spring.proxy; public interface UserManager { public void addUser(String id, String password); public void delUser(String id); }
package com.fy.spring.proxy; public class UserManagerImpl implements UserManager { public void addUser(String id, String password) { System.out.println(".: 掉用了UserManagerImpl.addUser()方法! "); } public void delUser(String id) { System.out.println(".: 掉用了UserManagerImpl.delUser()方法! "); } }
JDK動(dòng)態(tài)代理類(lèi)
package com.fy.spring.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * JDK動(dòng)態(tài)代理類(lèi) * * */ public class JDKProxy implements InvocationHandler { private Object targetObject;//需要代理的目標(biāo)對(duì)象 public Object newProxy(Object targetObject) {//將目標(biāo)對(duì)象傳入進(jìn)行代理 this.targetObject = targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);//返回代理對(duì)象 } public Object invoke(Object proxy, Method method, Object[] args)//invoke方法 throws Throwable { checkPopedom();//一般我們進(jìn)行邏輯處理的函數(shù)比如這個(gè)地方是模擬檢查權(quán)限 Object ret = null; // 設(shè)置方法的返回值 ret = method.invoke(targetObject, args); //調(diào)用invoke方法,ret存儲(chǔ)該方法的返回值 return ret; } private void checkPopedom() {//模擬檢查權(quán)限的例子 System.out.println(".:檢查權(quán)限 checkPopedom()!"); } }
CGLibProxy動(dòng)態(tài)代理類(lèi)
package com.fy.spring.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * CGLibProxy動(dòng)態(tài)代理類(lèi)的實(shí)例 * * */ public class CGLibProxy implements MethodInterceptor { private Object targetObject;// CGLib需要代理的目標(biāo)對(duì)象 public Object createProxyObject(Object obj) { this.targetObject = obj; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj.getClass()); enhancer.setCallback(this); Object proxyObj = enhancer.create(); return proxyObj;// 返回代理對(duì)象 } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object obj = null; if ("addUser".equals(method.getName())) {// 過(guò)濾方法 checkPopedom();// 檢查權(quán)限 } obj = method.invoke(targetObject, args); return obj; } private void checkPopedom() { System.out.println(".:檢查權(quán)限 checkPopedom()!"); } }
測(cè)試類(lèi):
public class Client { public static void main(String[] args) { UserManager userManager = (UserManager) new CGLibProxy() .createProxyObject(new UserManagerImpl()); System.out.println("-----------CGLibProxy-------------"); userManager.addUser("tom", "root"); System.out.println("-----------JDKProxy-------------"); JDKProxy jdkPrpxy = new JDKProxy(); UserManager userManagerJDK = (UserManager) jdkPrpxy .newProxy(new UserManagerImpl()); userManagerJDK.addUser("tom", "root"); } }
運(yùn)行結(jié)果:
-----------CGLibProxy-------------
檢查權(quán)限 checkPopedom()!
掉用了UserManagerImpl.addUser()方法!
-----------JDKProxy-------------
檢查權(quán)限 checkPopedom()!
掉用了UserManagerImpl.addUser()方法!
JDK代理是不需要以來(lái)第三方的庫(kù),只要要JDK環(huán)境就可以進(jìn)行代理,它有幾個(gè)要求
- 實(shí)現(xiàn)InvocationHandler
- 使用Proxy.newProxyInstance產(chǎn)生代理對(duì)象
- 被代理的對(duì)象必須要實(shí)現(xiàn)接口
CGLib 必須依賴(lài)于CGLib的類(lèi)庫(kù),但是它需要類(lèi)來(lái)實(shí)現(xiàn)任何接口代理的是指定的類(lèi)生成一個(gè)子類(lèi),覆蓋其中的方法,是一種繼承但是針對(duì)接口編程的環(huán)境下推薦使用JDK的代理
在Hibernate中的攔截器其實(shí)現(xiàn)考慮到不需要其他接口的條件Hibernate中的相關(guān)代理采用的是CGLib來(lái)執(zhí)行。
希望本文所述對(duì)你有所幫助,Spring中的兩種代理JDK和CGLIB的區(qū)別淺談內(nèi)容就給大家介紹到這里了。希望大家繼續(xù)關(guān)注我們的網(wǎng)站!想要學(xué)習(xí)java可以繼續(xù)關(guān)注本站。
相關(guān)文章
java使用jdbc操作數(shù)據(jù)庫(kù)示例分享
這篇文章主要介紹了java使用jdbc操作數(shù)據(jù)庫(kù)示例,需要的朋友可以參考下2014-03-03Spring?Aop+Redis實(shí)現(xiàn)優(yōu)雅記錄接口調(diào)用情況
通常情況下,開(kāi)發(fā)完一個(gè)接口,無(wú)論是在測(cè)試階段還是生產(chǎn)上線(xiàn),我們都需要對(duì)接口的執(zhí)行情況做一個(gè)監(jiān)控,所以本文為大家整理了Spring統(tǒng)計(jì)接口調(diào)用的多種方法,希望對(duì)大家有所幫助2023-06-06Java中Date時(shí)區(qū)的轉(zhuǎn)換代碼示例
這篇文章主要給大家介紹了關(guān)于Java中Date時(shí)區(qū)轉(zhuǎn)換的相關(guān)資料,當(dāng)在不同的時(shí)區(qū)使用相同程序,時(shí)間的值只會(huì)為當(dāng)?shù)貢r(shí)間,這樣就會(huì)造成時(shí)間混亂,需要的朋友可以參考下2023-07-07關(guān)于SSM框架下各層的解釋說(shuō)明(Controller等)
這篇文章主要介紹了關(guān)于SSM框架下各層的解釋說(shuō)明(Controller等),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02一次java異步任務(wù)的實(shí)戰(zhàn)記錄
最近做項(xiàng)目的時(shí)候遇到了一個(gè)小問(wèn)題,從前臺(tái)提交到服務(wù)端A,A調(diào)用服務(wù)端B處理超時(shí),下面這篇文章主要給大家介紹了一次java異步任務(wù)的實(shí)戰(zhàn)記錄,需要的朋友可以參考下2022-05-05Java?中?Class?Path?和?Package的使用詳解
這篇文章主要介紹了Java?中?Class?Path和Package的使用詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08