1. 程式人生 > >Spring AOP 代理實現的兩種方式: JDK動態代理 和 Cglib框架動態代理

Spring AOP 代理實現的兩種方式: JDK動態代理 和 Cglib框架動態代理

1.JDK動態代理

JDK API 內建 ---- 通過 Proxy類,為目標物件建立代理 (必須面向介面代理 ),此文中介面為UserDao,實現類為UserDaoImpl.

public class UserDaoImpl implements UserDao {

    @Override
    public void save() {
        System.out.println("儲存方法執行");

    }

}

下面為目標物件target(UserDaoImpl)建立代理的工廠:

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

public class JdkProxyFactory{
    //被代理的物件
    private Object target;
    
    public JdkProxyFactory(Object target) {
        this.target = target;
    }
    
    /**
     * 用於建立target的代理物件
     * @return
     */
    public Object createProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //執行增強的方法
                System.out.println("方法增強了");
                //執行被代理物件的真正方法
                return method.invoke(target, args);
            }
        });
    }

}
下面為測試類,為UserDaoImpl建立代理物件,對用代理物件執行UserDaoImpl中的方法
public class JdkProxyTest {
    @Test
    public void testUseProxy(){
        //代理之前的物件
        UserDao userDao = new UserDaoImpl();
        //代理之後的物件
        UserDao userDaoProxy = (UserDao) new JdkProxyFactory(userDao).createProxy();
        System.out.println(userDaoProxy);
        userDaoProxy.save();
    }

}

缺點: 使用Jdk動態代理,必須要求target目標物件,實現介面 ,如果沒有介面,不能使用Jdk動態代理


2.Cglib 動態代理

CGLIB(CodeGeneration Library)是一個開源專案!是一個強大的,高效能,高質量的Code生成類庫,它可以在執行期擴充套件Java類與實現Java介面。

Cglib 不但可以對介面進行代理,也可以對目標類物件,實現代理(解決了 Jdk 只能對介面代理問題 )。在spring3.2版本 core包中內建cglib 類。

下面為ProductDao使用cglib為其建立代理物件

public class ProductDao {
    
    public void saveProdcut(){
        System.out.println("產品儲存");
    }

}

建立代理物件的工廠:

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * 使用cglib進行代理 --- 工廠類
 * 
 */
public class CglibProxyFactory implements MethodInterceptor {
	// 被代理目標物件
	private Object target;

	// 在構造工廠時傳入被代理物件
	public CglibProxyFactory(Object target) {
		this.target = target;
	}

	// 建立代理物件方法
	public Object createProxy() {
		// 1、 建立Enhancer物件
		Enhancer enhancer = new Enhancer();

		// 2、 cglib建立代理,對目標物件,建立子類物件
		enhancer.setSuperclass(target.getClass());

		// 3、傳入 callback物件,對目標增強
		enhancer.setCallback(this);

		return enhancer.create();
	}

	@Override
	/**
	 * proxy 代理物件
	 * method 當前呼叫方法
	 * args 方法引數
	 * methodProxy 被呼叫方法代理物件 (作用:執行父類的方法)
	 */
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("記錄日誌......");
		// 按照JDK程式設計
		// return method.invoke(target, args);
		return methodProxy.invokeSuper(proxy, args);
	}
}


下面為測試類:

import org.junit.Test;

public class CglibProxyTest {
    @Test
    public void test(){
        ProductDao productDao = new ProductDao();
        ProductDao productDaoProxy = (ProductDao) new CglibProxyFactory(productDao).createCglibProxy();
        productDaoProxy.saveProdcut();
    }
}

執行結果如下:



Cglib 建立代理思想:對目標類建立子類物件

       設定 superClass 對哪個類建立子類 (類似 JDK代理 介面)

       設定 callback 實現增強程式碼 (類似 JDK代理InvocationHandler )

在cglib的callback函式中,要執行被代理物件的方法

       method.invoke(target,args); 等價於 methodProxy.invokeSuper(proxy,args); 




優先對介面代理(使用JDK代理),如果目標沒有介面,才會使用cglib代理