1.CGLIB包的介紹
代理爲控制要訪問的目標對象提供了一種途徑。當訪問對象時,它引入了一個間接的層。JDK自從1.3版本開始,就引入了動態代理,而且常常被用來動態地創 建代理。JDK的動態代理用起來很是簡單,當它有一個限制,就是使用動態代理的對象必須實現一個或多個接口。若是想代理沒有實現接口的繼承的類,該怎麼 辦?如今咱們可使用CGLIB包
CGLIB是一個強大的高性能的代碼生成包。它普遍的被許多AOP的框架使用,例如spring AOP和dynaop,爲他們提供方法的interception(攔截)。最流行的OR Mapping工具hibernate也使用CGLIB來代理單端single-ended(多對一和一對一)關聯(對集合的延遲抓取,是採用其餘機制實 現的)。EasyMock和jMock是經過使用模仿(moke)對象來測試Java代碼的包。它們都經過使用CGLIB來爲那些沒有接口的類建立模仿 (moke)對象。
CGLIB包的底層是經過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。除了CGLIB包,腳本語言例如 Groovy和BeanShell,也是使用ASM來生成java的字節碼。當不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文 件的格式和指令集都很熟悉。java
Figure 1: CGLIB Library and ASM Bytecode Frameworkspring
圖 一顯示了和CGLIB包和一些框架和語言的關係圖。須要注意的是一些框架例如Spring AOP和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動態代理方法快多了。app
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一般被首選使 用,由於它更快。在這個方法中,咱們能夠在調用原方法以前或以後注入本身的代碼。性能
Figure 3: CGLIB MethodInterceptor測試
net.sf.cglib.proxy.MethodInterceptor可以知足任何的攔截(interception )須要,當對有些狀況下可能過分。爲了簡化和提升性能,CGLIB包提供了一些專門的回調(callback)類型。例如:this
net.sf.cglib.proxy.FixedValue
net.sf.cglib.proxy.NoOp
net.sf.cglib.proxy.LazyLoader
net.sf.cglib.proxy.Dispatcher
net.sf.cglib.proxy.ProxyRefDispatcher
如 圖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 (jason@lizjason.com)
*/
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 (jason@lizjason.com)
*
*/
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);
}
}
咱們能夠看到在攔截方法中,首先進行權限的檢查,若是經過權限的檢查,攔截方法再調用目標對象的原始方法。因爲性能的緣由,對原始方法的調用咱們使用CGLIB的net.sf.cglib.proxy.MethodProxy對象,而不是反射中通常使用
java.lang.reflect.Method對象。
Use a CallbackFilter
net.sf.cglib.proxy.CallbackFilter容許咱們在方法層設置回調(callback)。假如你有一個PersistenceServiceImpl類,它有兩個方法:save和load,其中方法save須要權限檢查,而方法load不須要權限檢查。
package com.lizjason.cglibproxy.impl;
import com.lizjason.cglibproxy.PersistenceService;
/**
* A simple implementation of PersistenceService interface
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
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 forPersistenceServiceImpl
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
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()
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()