轉載自 cglib之Enhancerhtml
cglib庫的Enhancer在Spring AOP中做爲一種生成代理的方式被普遍使用。本文針對Enhancer的用法以實際代碼爲例做一些介紹。java
Enhancer是cglib中使用頻率很高的一個類,它是一個字節碼加強器,能夠用來爲無接口的類建立代理。它的功能與java自帶的Proxy類挺類似的。它會根據某個給定的類建立子類,而且全部非final的方法都帶有回調鉤子。數組
那麼Enhancer使用的Callback具體有哪些呢?下面介紹如下這幾種Callback。在cglib中Callback是一個標記接口,Enhancer使用的回調就是cglib中Callback接口的子接口。ide
方法攔截器。這個東西和JDK自帶的InvocationHandler很相似oop
Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable
這其中MethodProxy proxy參數通常是用來調用原來的對應方法的。好比能夠proxy.invokeSuper(obj, args)
。那麼爲何不能像InvocationHandler那樣用method來調用呢?由於若是用method調用會再次進入攔截器。爲了不這種狀況,應該使用接口方法中第四個參數methodProxy調用invokeSuper方法。ui
public class EnhancerTest { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Car.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before"); Object res = methodProxy.invokeSuper(obj, args); System.out.println("after"); return res; } }); Car car = (Car) enhancer.create(); car.print(); } static class Car { void print() { System.out.println("I am a car"); } } }
上面的程序會打印:this
before
I am a car
afterspa
這個回調至關簡單,就是啥都不幹的意思。代理
LazyLoader是cglib用於實現懶加載的callback。當被加強bean的方法初次被調用時,會觸發回調,以後每次再進行方法調用都是對LazyLoader第一次返回的bean調用。code
public class EnhancerTest { public static void main(String[] args) { CarFactory factory = new CarFactory(); System.out.println("factory built"); System.out.println(factory.car.getName()); System.out.println(factory.car.getName()); } static class Car { String name; Car() { } String getName() { return name; }
} static class CarFactory { Car car; CarFactory() { car = carLazyProxy(); } Car carLazyProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Car.class); enhancer.setCallback(new LazyLoader() { @Override public Object loadObject() throws Exception { System.out.println("prepare loading"); Car car = new Car(); car.name = "this is a car"; System.out.println("after loading"); return car; } }); return ((Car) enhancer.create()); } } }
上面的程序打印狀況以下:
factory built
prepare loading
after loading
this is a car
this is a car
Dispatcher和LazyLoader做用很類似,區別是用Dispatcher的話每次對加強bean進行方法調用都會觸發回調。
public class EnhancerTest { public static void main(String[] args) { CarFactory factory = new CarFactory(); System.out.println("factory built"); System.out.println(factory.car.getName()); System.out.println(factory.car.getName()); } static class Car { String name; Car() { } String getName() { return name; }
} static class CarFactory { Car car; CarFactory() { car = carLazyProxy(); } Car carLazyProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Car.class); enhancer.setCallback(new Dispatcher() { @Override public Object loadObject() throws Exception { System.out.println("prepare loading"); Car car = new Car(); car.name = "this is a car"; System.out.println("after loading"); return car; } }); return ((Car) enhancer.create()); } } }
程序會打印:
factory built
prepare loading
after loading
this is a car
prepare loading
after loading
this is a car
cglib的InvocationHandler和JDK自帶的InvocationHandler做用基本相同。使用的時候要注意,若是對參數中的method再次調用,會重複進入InvocationHandler。
public class EnhancerTest { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Car.class); enhancer.setCallback(new InvocationHandler() { @Override public Object invoke(Object obj, Method method, Object[] args) throws Throwable { if (method.getReturnType() == void.class) { System.out.println("hack"); } return null; } }); Car car = (Car) enhancer.create(); car.print(); } static class Car { void print() { System.out.println("I am a car"); } } }
上面的程序會打印:
hack
FixedValue通常用於替換方法的返回值爲回調方法的返回值,但必須保證返回類型是兼容的,不然會出轉換異常。
public class EnhancerTest { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Car.class); enhancer.setCallback(new FixedValue() { @Override public Object loadObject() throws Exception { return "hack!"; } }); Car car = (Car) enhancer.create(); System.out.println(car.print1()); System.out.println(car.print2()); } static class Car { String print1() { return "car1"; } String print2() { return "car2"; } } }
上面的代碼會打印:
hack!
hack!
上面已經介紹了Enhancer的幾種常見callback,這裏再介紹一下CallbackFilter。
上面都是爲加強bean配置了一種代理callback,可是當須要做一些定製化的時候,CallbackFilter就派上用處了。
當經過設置CallbackFilter加強bean以後,bean中原方法都會根據設置的filter與一個特定的callback映射。咱們一般會使用cglib中CallbackFilter的默認實現CallbackHelper,它的getCallbacks方法能夠返回生成的callback數組。
下面是CallbackFilter的demo程序。
public class EnhancerTest {
public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Car.class); CallbackHelper helper = new CallbackHelper(Car.class,new Class[0]) { @Override protected Object getCallback(Method method) { if (method.getReturnType() == void.class) { return new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before invocation"); Object res = methodProxy.invokeSuper(obj, args); System.out.println("after invocation"); return res; } }; } else if (method.getReturnType() == String.class) { return new FixedValue() { @Override public Object loadObject() throws Exception { return "a hacked car"; } }; } else return NoOp.INSTANCE; } }; enhancer.setCallbacks(helper.getCallbacks()); enhancer.setCallbackFilter(helper); Car car = (Car) enhancer.create(); car.print(); System.out.println(car.getId()); System.out.println(car.getName()); } static class Car { static int index = 0; int id; Car() { id = index++; } String getName() { return "car"; } int getId() { return id; } void print() { System.out.println("I am a car"); } } }
程序將打印:
before invocation
I am a car
after invocation
0
a hacked car
咱們能夠看看CallbackHelper的源碼在作什麼事情:
public CallbackHelper(Class superclass, Class[] interfaces) { List methods = new ArrayList(); Enhancer.getMethods(superclass, interfaces, methods); Map indexes = new HashMap(); for (int i = 0, size = methods.size(); i < size; i++) { Method method = (Method)methods.get(i); // getCallback就是咱們編寫的根據method返回callback的策略方法。 Object callback = getCallback(method); if (callback == null) throw new IllegalStateException("getCallback cannot return null"); boolean isCallback = callback instanceof Callback; if (!(isCallback || (callback instanceof Class))) throw new IllegalStateException("getCallback must return a Callback or a Class"); if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback)) throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method"); // 從callback與編號的map中獲取編號。 Integer index = (Integer)indexes.get(callback); // 若是map中沒有對應callback,則插入到map中。 if (index == null) { index = new Integer(callbacks.size()); indexes.put(callback, index); } // 維護bean的method與callback編號的映射。 methodMap.put(method, index); // 維護callback列表。 callbacks.add(callback); } }
能夠看到在CallbackHelper源碼中也是維護了一個methodMap用於保存method和callback編號的映射,一個callbacks用於保存callback集合(方便getCallbacks方法導出)。