Cglib之Enhancer建立動態代理
CGLIB是一個強大、高效能的位元組碼生成庫,它用於在執行時擴充套件Java類和實現介面;本質上它是通過動態的生成一個子類去覆蓋所要代理的類(非final修飾的類和方法)。Enhancer是一個非常重要的類,它允許為非介面型別建立一個JAVA代理,Enhancer動態的建立給定類的子類並且攔截代理類的所有的方法,和JDK動態代理不一樣的是不管是介面還是類它都能正常工作。
- net.sf.cglib.proxy.Callback介面:在cglib包中是一個很關鍵的介面,所有被net.sf.cglib.proxy.Enhancer類呼叫的回撥(callback)介面都要繼承這個介面。
- net.sf.cglib.proxy.MethodInterceptor介面:是通用的回撥(callback)型別,他經常被AOP用來實現攔截(intercept)方法的呼叫;
MethodInterceptor介面只定義了一個方法:
package net.sf.cglib.proxy; import java.lang.reflect.Method; public abstract interface MethodInterceptor extends Callback { public abstract Object intercept(Object paramObject, Method paramMethod, Object[] paramArrayOfObject, MethodProxy paramMethodProxy) throws Throwable; }
1.AOP模擬場景示例
建立一個HelloWorld類:
package com.test;
public class HelloWorld {
public String say(boolean say) throws Exception {
System.out.println("Hello Student");
if(!say) {
throw new Exception("回答錯誤!");
}
return "回答正確!";
}
}
建立Enhancer增強代理類的回撥ProxyFactory類:
package controllers; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * 所有方法的代理的回撥 * * @version 1.0 * @since JDK1.7 * @author yaomy * @company 上海朝陽永續資訊科技有限公司 * @date 2018年9月18日 上午10:38:40 */ public class ProxyFactory implements MethodInterceptor { //要代理的真實物件 private Object obj; public Object createProxy(Object target) { this.obj = target; Enhancer enhancer = new Enhancer(); //設定代理目標 enhancer.setSuperclass(this.obj.getClass()); //設定單一回調物件,在呼叫中攔截對目標方法的呼叫 enhancer.setCallback(this); //設定類載入器 enhancer.setClassLoader(this.obj.getClass().getClassLoader()); return enhancer.create(); } /** * * 方法描述 當對基於代理的方法回撥時,在呼叫原方法之前會呼叫該方法 * 攔截對目標方法的呼叫 * * @param obj 代理物件 * @param method 攔截的方法 * @param args 攔截的方法的引數 * @param proxy 代理 * @return * @throws Throwable * * @author yaomy * @date 2018年9月18日 上午11:03:27 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object result = null; try { //前置通知 before(); result = proxy.invokeSuper(obj, args); //後置通知 after(); } catch (Exception e) { //異常通知 exception(); } finally { //方法返回前通知 beforeReturning(); } return result; } private void before() { System.out.println("before method invoke..."); } private void after() { System.out.println("after method invoke..."); } private void exception() { System.out.println("exception method invoke..."); } private void beforeReturning() { System.out.println("beforeReturning method invoke..."); } }
模擬場景類:
package controllers;
public class EnhancerTest {
public static void main(String[] args) throws Exception {
HelloWorld hello = new HelloWorld();
ProxyFactory proxy = new ProxyFactory();
HelloWorld world = (HelloWorld)proxy.createProxy(hello);
String result = world.say(false);
System.out.println(result);
}
}
輸出結果是:
before method invoke...
Hello Student
after method invoke...
beforeReturning method invoke...
回答正確!
把true改為false的結果是:
before method invoke...
Hello Student
exception method invoke...
beforeReturning method invoke...
null
2.net.sf.cglib.proxy.MethodInterceptor能夠滿足任何的攔截需求,但是對於有些情況可能有點過度;為了簡化和提高效能,cglib包提供了一些專門的回撥(callback)型別。
net.sf.cglib.proxy.FixedValue:為提高效能,FixedValue回撥對強制某一特定方法返回固定值。
package controllers;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;
public class Test {
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
Enhancer enhancer = new Enhancer();
//設定代理目標
enhancer.setSuperclass(hello.getClass());
//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
// TODO Auto-generated method stub
return "FixedValue";
}
});
//設定類載入器
enhancer.setClassLoader(hello.getClass().getClassLoader());
Object obj = enhancer.create();
System.out.println(obj);
}
}
net.sf.cglib.proxy.NoOp:NoOp回撥把對方法呼叫直接委派到這個方法在父類中的實現(也可以理解成真實物件直接呼叫方法);
public static void main(String[] args) throws Exception {
HelloWorld hello = new HelloWorld();
Enhancer enhancer = new Enhancer();
//設定代理目標
enhancer.setSuperclass(hello.getClass());
//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
enhancer.setCallback(NoOp.INSTANCE);
//設定類載入器
enhancer.setClassLoader(hello.getClass().getClassLoader());
HelloWorld obj = (HelloWorld)enhancer.create();
System.out.println(obj.say(true));
}
net.sf.cglib.proxy.LazyLoader:當實際的物件需要延遲裝載時,可以使用LazyLoader回撥。一旦實際物件被裝載,它將被每一個呼叫代理物件的方法使用; net.sf.cglib.proxy.Dispatcher:Dispathcer回撥和LazyLoader回撥有相同的特點,不同的是,當代理方法被呼叫時,裝載物件的方法也總要被呼叫; net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回撥和Dispatcher一樣,不同的是,它可以把代理物件作為裝載物件方法的一個引數傳遞;
3.使用CallbackFilter設定回撥
net.sf.cglib.proxy.CallbackFilter允許我們在方法層設定回撥(callback),根據我們對方法處理的需求設定不同的回撥;如下有一個類HelloWorld,裡面有兩個方法save和update,save方法需要做前置和後置處理,但是update方法不需要:
package controllers;
public class HelloWorld {
public String save() {
System.out.println("save...");
return "save";
}
public String update() {
System.out.println("update...");
return "update";
}
}
package controllers;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
/**
*
* 回撥過濾器
*
* @version 1.0
* @since JDK1.7
* @date 2018年9月19日 上午11:02:12
*/
public class CallBackFilterTest implements CallbackFilter{
/**
* 方法返回的值是和callback回撥介面陣列一一對應的陣列下標
*/
@Override
public int accept(Method method) {
String name = method.getName();
if("save".equals(name)) {
return 0;
}
return 1;
}
}
accept方法中對代理方法和回撥進行了匹配,返回的值是某個方法在回撥陣列中的索引;下面是Test類的示例,在這個例子中save方法使用了ProxyFactory回撥,update方法使用了NoOp回撥;:
package controllers;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;
import net.sf.cglib.proxy.NoOp;
public class Test {
public static void main(String[] args) throws Exception {
HelloWorld hello = new HelloWorld();
Enhancer enhancer = new Enhancer();
//設定代理目標
enhancer.setSuperclass(hello.getClass());
//設定呼叫過濾器
CallbackFilter filter = new CallBackFilterTest();
enhancer.setCallbackFilter(filter);
//建立各個目標代理方法的回撥,回撥的順序要與過濾器索引一致
Callback callbacks[] = new Callback[] {new ProxyFactory(), NoOp.INSTANCE};
//設定單一回調物件,在呼叫中攔截對目標方法的呼叫
// enhancer.setCallback(NoOp.INSTANCE);
enhancer.setCallbacks(callbacks);
//設定類載入器
enhancer.setClassLoader(hello.getClass().getClassLoader());
HelloWorld obj = (HelloWorld)enhancer.create();
System.out.println(obj.update());
System.out.println("=============");
System.out.println(obj.save());
}
}
輸出結果如下:
update...
update
=============
before method invoke...
save...
after method invoke...
beforeReturning method invoke...
save