Spring的兩種動態代理:JDK動態代理和cglib動態代理

一、原理區別:

java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。

而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

1、如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP
2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP

3、如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

如何強制使用CGLIB實現AOP?
(1)添加CGLIB庫,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

JDK動態代理和CGLIB字節碼生成的區別?
(1)JDK動態代理只能對實現了接口的類生成代理,而不能針對類
(2)CGLIB是針對實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
因爲是繼承,所以該類或方法最好不要聲明成final

二、代碼實現
用戶管理接口

 

package com.lf.shejimoshi.proxy.entity;
//用戶管理接口
public interface UserManager {
    //新增用戶抽象方法
    void addUser(String userName,String password);
    //刪除用戶抽象方法
    void delUser(String userName);
    
}

用戶管理接口實現類

 

package com.lf.shejimoshi.proxy.entity;
//用戶管理實現類,實現用戶管理接口
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);
    }
    
}

JDK動態代理

 

package com.lf.shejimoshi.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.lf.shejimoshi.proxy.entity.UserManager;
import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
//JDK動態代理實現InvocationHandler接口
public class JdkProxy implements InvocationHandler {
    private Object target ;//需要代理的目標對象
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK動態代理,監聽開始!");
        Object result = method.invoke(target, args);
        System.out.println("JDK動態代理,監聽結束!");
        return result;
    }
    //定義獲取代理對象方法
    private Object getJDKProxy(Object targetObject){
        //爲目標對象target賦值
        this.target = targetObject;
        //JDK動態代理只能針對實現了接口的類進行代理,newProxyInstance 函數所需參數就可看出
        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");//執行新增方法
    }
    
}

JDK動態代理運行結果

 

image.png

Cglib動態代理(需要導入兩個jar包,asm-5.2.jar,cglib-3.2.5.jar。版本自行選擇)

 

package com.lf.shejimoshi.proxy.cglib;

import java.lang.reflect.Method;

import com.lf.shejimoshi.proxy.entity.UserManager;
import com.lf.shejimoshi.proxy.entity.UserManagerImpl;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//Cglib動態代理,實現MethodInterceptor接口
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動態代理,監聽開始!");
        Object invoke = method.invoke(target, arr);//方法執行,參數:target 目標對象 arr參數數組
        System.out.println("Cglib動態代理,監聽結束!");
        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();//創建並返回代理對象
        return result;
    }
    
    public static void main(String[] args) {
        CglibProxy cglib = new CglibProxy();//實例化CglibProxy對象
        UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());//獲取代理對象
        user.delUser("admin");//執行刪除方法
    }
    
}

Cglib動態代理運行結果

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章