JDK動態(tài)代理:
利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
CGLiB動態(tài)代理:
利用ASM開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理。
何時使用JDK還是CGLiB?
1、如果目標對象實現了接口,默認情況下會采用JDK的動態(tài)代理實現AOP。
2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP。
3、如果目標對象沒有實現了接口,必須采用CGLIB庫,Spring會自動在JDK動態(tài)代理和CGLIB之間轉換。
如何強制使用CGLIB實現AOP?
1、添加CGLIB庫(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
2、在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK動態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別?
1、JDK動態(tài)代理只能對實現了接口的類生成代理,而不能針對類。
2、CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,并覆蓋其中方法實現增強,但是因為采用的是繼承,所以該類或方法最好不要聲明成final,對于final類或方法,是無法繼承的。
CGlib比JDK快?
1、使用CGLib實現動態(tài)代理,CGLib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術生成代理類,
在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能對聲明為final的方法進行代理,
因為CGLib原理是動態(tài)生成被代理類的子類。
2、在jdk6、jdk7、jdk8逐步對JDK動態(tài)代理優(yōu)化之后,在調用次數較少的情況下,JDK代理效率高于CGLIB代理效率,只有當進行大量調用的時候,jdk6和jdk7比CGLIB代理效率低一點,但是到jdk8的時候,jdk代理效率高于CGLIB代理,總之,每一次jdk版本升級,jdk代理效率都得到提升,而CGLIB代理消息確有點跟不上步伐。
Spring如何選擇用JDK還是CGLiB?
1、當Bean實現接口時,Spring就會用JDK的動態(tài)代理。
2、當Bean沒有實現接口時,Spring使用CGlib是實現。
3、可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
JDK動態(tài)代理實現
public interface UserManager {
//新增用戶抽象方法
void addUser(String userName,String password);
//刪除用戶抽象方法
void delUser(String userName);
}
public class UserManagerImpl implements UserManager {
//重寫新增用戶方法
@Override
public void addUser(String userName, String password) {
System.out.println("調用了新增的方法!");
System.out.println("傳入參數為 userName: "+userName+" password: "+password);
}
//重寫刪除用戶方法
@Override
public void delUser(String userName) {
System.out.println("調用了刪除的方法!");
System.out.println("傳入參數為 userName: "+userName);
}
}
public class JdkProxy implements InvocationHandler {
private Object target;//需要代理的目標對象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK動態(tài)代理,監(jiān)聽開始!");
Object result = method.invoke(target, args);
System.out.println("JDK動態(tài)代理,監(jiān)聽結束!");
return result;
}
//定義獲取代理對象方法
private Object getJDKProxy(Object targetObject) {
//為目標對象target賦值
this.target = targetObject;
//JDK動態(tài)代理只能針對實現了接口的類進行代理,newProxyInstance 函數所需參數就可看出
System.out.println(this.getClass());
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();//實例化JDKProxy對象
UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//獲取代理對象
user.addUser("admin", "123123");//執(zhí)行新增方法
}
}
CGlib實現
public class CglibProxy implements MethodInterceptor {
private Object target;//需要代理的目標對象
//重寫攔截方法
@Override
public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
System.out.println("Cglib動態(tài)代理,監(jiān)聽開始!");
Object invoke = method.invoke(target, arr);//方法執(zhí)行,參數:target 目標對象 arr參數數組
System.out.println("Cglib動態(tài)代理,監(jiān)聽結束!");
return invoke;
}
//定義獲取代理對象方法
public Object getCglibProxy(Object objectTarget){
//為目標對象target賦值
this.target = objectTarget;
Enhancer enhancer = new Enhancer();
//設置父類,因為Cglib是針對指定的類生成一個子類,所以需要指定父類
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);// 設置回調
Object result = enhancer.create();//創(chuàng)建并返回代理對象
return result;
}
public static void main(String[] args) {
CglibProxy cglib = new CglibProxy();//實例化CglibProxy對象
UserManager user = (UserManager) cglib.getCglibProxy(new UserManagerImpl());//獲取代理對象
user.delUser("admin");//執(zhí)行刪除方法
}
}