1. 背景
cglib庫的Enhancer在Spring AOP中作為一種生成代理的方式被廣泛使用。本文針對Enhancer的用法以實際代碼為例作一些介紹。
2. Enhancer是啥
Enhancer是cglib中使用頻率很高的一個類,它是一個字節碼增強器,可以用來為無接口的類創建代理。它的功能與java自帶的Proxy類挺相似的。它會根據某個給定的類創建子類,並且所有非final的方法都帶有回調鈎子。
2.1 Callback
那么Enhancer使用的Callback具體有哪些呢?下面介紹以下這幾種Callback。在cglib中Callback是一個標記接口,Enhancer使用的回調就是cglib中Callback接口的子接口。
2.1.1 Callback-MethodInterceptor
方法攔截器。這個東西和JDK自帶的InvocationHandler很類似
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方法。
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");
}
}
}
上面的程序會打印:
before
I am a car
after
2.1.2 Callback-NoOp
這個回調相當簡單,就是啥都不干的意思。
Callback-LazyLoader
LazyLoader是cglib用於實現懶加載的callback。當被增強bean的方法初次被調用時,會觸發回調,之后每次再進行方法調用都是對LazyLoader第一次返回的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 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
2.1.3 Callback-Dispatcher
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
2.1.4 Callback-InvocationHandler
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
2.1.5 Callback-FixedValue
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!
2.2 CallbackFilter
上面已經介紹了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方法導出)。