cglib 介紹 原理 使用 demo examples

 翻譯 原文:http://www.ociweb.com/jnb/jnbNov2005.html

1.CGLIB包的介紹
    代理爲控制要訪問的目標對象提供了一種途徑。當訪問對象時,它引入了一個間接的層。JDK自從1.3版本開始,就引入了動態代理,並且經常被用來動態地創建代理。JDK的動態代理用起來非常簡單,當它有一個限制,就是使用動態代理的對象必須實現一個或多個接口。如果想代理沒有實現接口的繼承的類,該怎麼辦?現在我們可以使用CGLIB包
    CGLIB是一個強大的高性能的代碼生成包。它廣泛的被許多AOP的框架使用,例如SpringAOP和dynaop,爲他們提供方法的interception(攔截)。最流行的ORMapping工具hibernate也使用CGLIB來代理單端single-ended(多對一和一對一)關聯(對集合的延遲抓取,是採用其他機制實現的)。EasyMock和jMock是通過使用模仿(moke)對象來測試java代碼的包。它們都通過使用CGLIB來爲那些沒有接口的類創建模仿(moke)對象。
    CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。除了CGLIB包,腳本語言例如Groovy和BeanShell,也是使用ASM來生成java的字節碼。當不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。

CGLIB and ASM
Figure 1: CGLIB Library and ASM Bytecode Framework
圖一顯示了和CGLIB包和一些框架和語言的關係圖。需要注意的是一些框架例如SpringAOP和Hibernate,它們爲了滿足需要經常同時使用JDK的動態代理和CGLIB包。Hiberater使用JDK的動態代理實現一個專門爲WebShere應用服務器的事務管理適配器;Spring AOP,如果不強制使用CGLIB包,默認情況是使用JDK的動態代理來代理接口。

2.CGLIB 代理的APIS
    CGLIB包的基本代碼很少,當學起來有一定的困難,主要是缺少文檔,這也是開源軟件的一個不足之處。目前CGLIB的版本是(2.1.2),主要由一下部分組成:
net.sf.cglib.core
底層字節碼處理類,他們大部分與ASM有關係。
• net.sf.cglib.transform
編譯期或運行期類和類文件的轉換
• net.sf.cglib.proxy
實現創建代理和方法攔截器的類
• net.sf.cglib.reflect
實現快速反射和C#風格代理的類
• net.sf.cglib.util
集合排序工具類
• net.sf.cglib.beans
JavaBean相關的工具類

大多時候,僅僅爲了動態地創建代理,你僅需要使用到代理包中很少的一些API。

正如我們先前所討論的,CGLIB包是在ASM之上的一個高級別的層。對代理那些沒有實現接口的類非常有用。本質上,它是通過動態的生成一個子類去覆蓋所要代理類的不是final的方法,並設置好callback,則原有類的每個方法調用就會轉變成調用用戶定義的攔截方法(interceptors),這比JDK動態代理方法快多了。

CGLIB APIs commonly used for proxying classes
Figure 2: CGLIB library APIs commonly used for proxying classes

圖2爲我們演示了創建一個具體類的代理時,通常要用到的CGLIB包的APIs。net.sf.cglib.proxy.Callback接口在CGLIB包中是一個很關鍵的接口,所有被net.sf.cglib.proxy.Enhancer類調用的回調(callback)接口都要繼承這個接口。net.sf.cglib.pr用oxy.MethodInterceptor接口是最通用的回調(callback)類型,它經常被基於代理的AOP用來實現攔截(intercept)方法的調用。這個接口只定義了一個方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

    當net.sf.cglib.proxy.MethodInterceptor做爲所有代理方法的回調(callback)時,當對基於代理的方法調用時,在調用原對象的方法的之前會調用這個方法,如圖3所示。第一個參數是代理對像,第二和第三個參數分別是攔截的方法和方法的參數。原來的方法可能通過使用java.lang.reflect.Method對象的一般反射調用,或者使用net.sf.cglib.proxy.MethodProxy對象調用。net.sf.cglib.proxy.MethodProxy通常被首選使用,因爲它更快。在這個方法中,我們可以在調用原方法之前或之後注入自己的代碼。
CGLIB MethodInterceptor
Figure 3: CGLIB MethodInterceptor

    net.sf.cglib.proxy.MethodInterceptor能夠滿足任何的攔截(interception )需要,當對有些情況下可能過度。爲了簡化和提高性能,CGLIB包提供了一些專門的回調(callback)類型。例如:
net.sf.cglib.proxy.FixedValue
爲提高性能,FixedValue回調對強制某一特別方法返回固定值是有用的。 net.sf.cglib.proxy.NoOp
NoOp回調把對方法調用直接委派到這個方法在父類中的實現。 net.sf.cglib.proxy.LazyLoader
當實際的對象需要延遲裝載時,可以使用LazyLoader回調。一旦實際對象被裝載,它將被每一個調用代理對象的方法使用。 net.sf.cglib.proxy.Dispatcher
Dispathcer回調和LazyLoader回調有相同的特點,不同的是,當代理方法被調用時,裝載對象的方法也總要被調用。 net.sf.cglib.proxy.ProxyRefDispatcher
ProxyRefDispatcher回調和Dispatcher一樣,不同的是,它可以把代理對象作爲裝載對象方法的一個參數傳遞。

如圖3所示,代理類的所以方法經常會用到回調(callback),當是你也可以使用net.sf.cglib.proxy.CallbackFilter有選擇的對一些方法使用回調(callback),這種考慮周詳的控制特性在JDK的動態代理中是沒有的。在JDK代理中,對java.lang.reflect.InvocationHandler方法的調用對代理類的所以方法都有效。

除了代理類外,CGLIB通過提供一個對java.lang.reflect.Proxy的drop-in替代來實現對對接口的代理。因爲這種代理能力的替代很少被用到,因此相應的APIs也很少提到。

CGLIB的代理包也對net.sf.cglib.proxy.Mixin提供支持。基本上,它允許多個對象被綁定到一個單個的大對象。在代理中對方法的調用委託到下面相應的對象中。

接下來我們看看如何使用CGLIB代理APIs創建代理。

創建一個簡單的代理CGLIB代理最核心的是net.sf.cglib.proxy.Enhancer類,爲了創建一個代理,最起碼你要用到這個類。首先,讓我們使用NoOp回調創建一個代理:
/**
* Create a proxy using NoOp callback. The target class
* must have a default zero-argument constructor.
*
* @param targetClass the super class of the proxy
* @return a new proxy for a target class instance
*/
public Object createProxy(Class targetClass) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(targetClass);
    enhancer.setCallback(NoOp.INSTANCE);
    return enhancer.create();
}
    返回值是target類一個實例的代理。在這個例子中,我們爲net.sf.cglib.proxy.Enhancer配置了一個單一的回調(callback)。我們可以看到很少直接創建一個簡單的代理,而是創建一個net.sf.cglib.proxy.Enhancer的實例,在net.sf.cglib.proxy.Enhancer類中你可使用靜態幫助方法創建一個簡單的代理。一般推薦使用上面例子的方法創建代理,因爲它允許你通過配置net.sf.cglib.proxy.Enhancer實例很好的控制代理的創建。

    要注意的是,target類是作爲產生的代理的父類傳進來的。不同於JDK的動態代理,它不能在創建代理時傳target對象,target對象必須被CGLIB包來創建。在這個例子中,默認的無參數構造器時用來創建target實例的。如果你想用CGLIB來創建有參數的實例,用net.sf.cglib.proxy.Enhancer.create(Class[],Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就可以了。方法中第一個參數定義了參數的類型,第二個是參數的值。在參數中,基本類型應被轉化成類的類型。

Use a MethodInterceptor爲了更好的使用代理,我們可以使用自己定義的MethodInterceptor類型回調(callback)來代替net.sf.cglib.proxy.NoOp回調。當對代理中所有方法的調用時,都會轉向MethodInterceptor類型的攔截(intercept)方法,在攔截方法中再調用底層對象相應的方法。下面我們舉個例子,假設你想對目標對象的所有方法調用進行權限的檢查,如果沒有經過授權,就拋出一個運行時的異常AuthorizationException。其中AuthorizationService.java接口的代碼如下:

package com.lizjason.cglibproxy;

import java.lang.reflect.Method;

/**
* A simple authorization service for illustration purpose.
*
* @author Jason Zhicheng Li ([email protected])
*/
public interface AuthorizationService {
    /**
    * Authorization check for a method call. An
AuthorizationException
    * will be thrown if the check fails.
    */
    void authorize(Method method);
}

net.sf.cglib.proxy.MethodInterceptor接口的實現的類AuthorizationInterceptor.java代碼如下:

package com.lizjason.cglibproxy.impl;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import com.lizjason.cglibproxy.AuthorizationService;

/**

* A simple MethodInterceptor implementation to

* apply authorization checks for proxy method calls.

*

* @author Jason Zhicheng Li ([email protected])

*

*/

public class AuthorizationInterceptor implements MethodInterceptor {

private AuthorizationService authorizationService;

/**

* Create a AuthorizationInterceptor with the given

* AuthorizationService

*/

public AuthorizationInterceptor (AuthorizationService authorizationService) {

this.authorizationService = authorizationService;

}

/**

* Intercept the proxy method invocations to inject authorization check.

* The original method is invoked through MethodProxy.

* @param object the proxy object

* @param method intercepted Method

* @param args arguments of the method

* @param proxy the proxy used to invoke the original method

* @throws Throwable any exception may be thrown; if so, super method will not be invoked

* @return any value compatible with the signature of the proxied method.

*/

public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {

if (authorizationService != null) {

//may throw an AuthorizationException if authorization failed

authorizationService.authorize(method);

}

return methodProxy.invokeSuper(object, args);

}

}


我們可以看到在攔截方法中,首先進行權限的檢查,如果通過權限的檢查,攔截方法再調用目標對象的原始方法。由於性能的原因,對原始方法的調用我們使用CGLIBnet.sf.cglib.proxy.MethodProxy對象,而不是反射中一般使用
java.lang.reflect.Method
對象。


Use a CallbackFilter

net.sf.cglib.proxy.CallbackFilter允許我們在方法層設置回調(callback)。假如你有一個PersistenceServiceImpl類,它有兩個方法:saveload,其中方法save需要權限檢查,而方法load不需要權限檢查。



package com.lizjason.cglibproxy.impl;

import com.lizjason.cglibproxy.PersistenceService;

/**

* A simple implementation of PersistenceService interface

*

* @author Jason Zhicheng Li ([email protected])

*/

public class PersistenceServiceImpl implements PersistenceService {

public void save(long id, String data) {

System.out.println(data + " has been saved successfully.");

}

public String load(long id) {

return "Jason Zhicheng Li";

}

}

注意到PersistenceServiceImpl類實現了PersistenceService 接口,因此沒有要求要使用CGLIB創建代理。
net.sf.cglib.proxy.CallbackFilter
接口的實現如下:

package com.lizjason.cglibproxy.impl;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.CallbackFilter;

/**

* An implementation of CallbackFilter for PersistenceServiceImpl

*

* @author Jason Zhicheng Li ([email protected])

*/

public class PersistenceServiceCallbackFilter implements CallbackFilter {

//callback index for save method

private static final int SAVE = 0;

//callback index for load method

private static final int LOAD = 1;

/**

* Specify which callback to use for the method being invoked.

* @method the method being invoked.

* @return the callback index in the callback array for this method

*/

public int accept(Method method) {

String name = method.getName();

if ("save".equals(name)) {

return SAVE;

}

// for other methods, including the load method, use the

// second callback

return LOAD;

}

}


accept
方法中對代理方法和回調進行了匹配,返回的值是某方法在回調數組中的索引。下面是PersistenceServiceImpl類代理的實現。


...

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(PersistenceServiceImpl.class);

CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();

enhancer.setCallbackFilter(callbackFilter);

AuthorizationService authorizationService = ...

Callback saveCallback = new AuthorizationInterceptor(authorizationService);

Callback loadCallback = NoOp.INSTANCE;

Callback[] callbacks = new Callback[]{saveCallback, loadCallback };

enhancer.setCallbacks(callbacks);

...

return (PersistenceServiceImpl)enhancer.create();

在這個例子中save方法使用了AuthorizationInterceptor實例,load方法使用了NoOp實例。此外,你也可以通過
net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])
方法指定代理對象所實現的接口。

除了爲net.sf.cglib.proxy.Enhancer指定回調數組,你還可以通過net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回調類型數組。當創建代理時,如果你沒有回調實例的數組,就可以使用回調類型。象使用回調一樣,你必須使用,net.sf.cglib.proxy.CallbackFilter爲每一個方法指定一個回調類型索引。你可以從http://www.lizjason.com/downloads/下載設置回調類型和接口的完整代碼。

cglib 的幾個測試demo

增強一個已有類

public class MyClass {
 
	public void method() {
		System.out.println("MyClass.method()");
	}
}
import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
 
public class Main {
 
	public static void main(String[] args) {
 
		Enhancer enhancer = new Enhancer();
 
		enhancer.setSuperclass(MyClass.class);
		enhancer.setCallback( new MethodInterceptorImpl() );
 
 
		MyClass my = (MyClass)enhancer.create();
 
		my.method();
	}
 
	private static class MethodInterceptorImpl implements MethodInterceptor {
		
		public Object intercept(Object obj, 
                                Method method, 
                                Object[] args, 
                                MethodProxy proxy) throws Throwable {
 
			System.out.println(method);
 
			proxy.invokeSuper(obj, args);
 
			return null;
		}
	}
}

執行結果:

public void cglib_test.MyClass.method()
MyClass.method()

使用CallbackFilter

public class MyClass {
 
	public void method() {
		System.out.println("MyClass.method()");
	}
 
	public void method2() {
		System.out.println("MyClass.method2()");
	}
}
import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.NoOp;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
 
 
public class Main {
 
	public static void main(String[] args) {
 
		Callback[] callbacks =
			new Callback[] { new MethodInterceptorImpl(),  NoOp.INSTANCE };
 
		Enhancer enhancer = new Enhancer();
 
		enhancer.setSuperclass(MyClass.class);
		enhancer.setCallbacks( callbacks );
		enhancer.setCallbackFilter( new CallbackFilterImpl() );
 
 
		MyClass my = (MyClass)enhancer.create();
 
		my.method();
		my.method2();
	}
 
	private static class CallbackFilterImpl implements CallbackFilter {
 
		public int accept(Method method) {
 
			if ( method.getName().equals("method2") ) {
				return 1;
 
			} else {
				return 0;
			}
		}
	}
 
	private static class MethodInterceptorImpl implements MethodInterceptor {
		
		public Object intercept(Object obj, 
                                Method method, 
                                Object[] args, 
                                MethodProxy proxy) throws Throwable {
 
			System.out.println(method);
 
			return proxy.invokeSuper(obj, args);
		}
	}
}

執行結果:

public void cglib_test.MyClass.method()
MyClass.method()
MyClass.method2()

使用Mixin

public interface MyInterfaceA {
 
	public void methodA();
}
 
public interface  MyInterfaceB {
	public void methodB();
}
 
public class MyInterfaceAImpl implements MyInterfaceA {
 
	public void methodA() {
		System.out.println("MyInterfaceAImpl.methodA()");
	}
}
 
public class MyInterfaceBImpl implements MyInterfaceB {
 
	public void methodB() {
		System.out.println("MyInterfaceBImpl.methodB()");
	}
}

import net.sf.cglib.proxy.Mixin;
 
public class Main {
 
	public static void main(String[] args) {
 
		Class[] interfaces =
			new Class[] { MyInterfaceA.class, MyInterfaceB.class };
		
		Object[] delegates =
			new Object[] { new MyInterfaceAImpl(), new MyInterfaceBImpl() };
			
		
		Object obj = Mixin.create(interfaces, delegates);
 
 
		MyInterfaceA myA = (MyInterfaceA)obj;
		myA.methodA();
 
 
		MyInterfaceB myB = (MyInterfaceB)obj;
		myB.methodB();
	}
}

執行結果:

MyInterfaceAImpl.methodA()
MyInterfaceBImpl.methodB()

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