参考链接:cglib: The missing manual
CGLib(Code Generation Library)同JDK动态代理的目的一致,在运行时生成代理类字节码加载到JVM中,并生成代理对象。CGLib底层基于ASM(一个Java字节码操控框架),即能够为接口生成代理,又能为类生成代理。首先看一个例子:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
public class ProxyTarget {
public String method1(String param) {
System.out.println("ProxyTarget:method(), param: " + param);
return param;
}
}
@Test(expected = ClassCastException.class)
public void testFixedValue() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProxyTarget.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello, CGLib!";
}
});
ProxyTarget proxy = (ProxyTarget) enhancer.create();
System.out.println(proxy.method1(null)); // Hello, CGLib!
System.out.println(proxy.toString()); // Hello, CGLib!
// Object.getClass()是final方法,不进行代理
System.out.println(proxy.getClass()); // ProxyTarget$ $EnhancerByCGLIB$$c1696218
System.out.println(proxy.hashCode()); // ClassCastException: String cannot be cast to Number
}
Enhancer类相当于JDK中的Proxy类,用来生成代理对象的核心类。第一步:设置了要代理的类:setSuperclass()。第二步设置代理回调处理接口:setCallback()。第三步:生成代理对象。第二步中,设置的回调匿名内部类FixedValue接口继承了Callback接口。表明对代理的方法均返回固定值。
CGLib生成的代理对象不能够对final修饰的方法进行代理(如Object的getClass方法),执行这些方法跟直接执行效果相同。而执行Object的hashCode()方法(不是final的)时,可以在运行时得到ClassCastException异常。
回调接口还可以设置为 InvocationHandler(CGLib库中作为JDK的InvocationHandler的替代)、MethodInterceptor等。使用InvocationHandler时要注意会导致递归调用,因为每一次对方法的调用都会被转发到同一个InvocationHandler对象的invoke方法中。
@Test(expected = RuntimeException.class)
public void testInvocationHandler() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProxyTarget.class);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 此方法里不能调用method.invoke,会导致递归调用
// Object result = method.invoke(proxy, method);
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello, CGLib!";
} else {
throw new RuntimeException("Don not know what to do.");
}
}
});
ProxyTarget proxy = (ProxyTarget) enhancer.create();
System.out.println(proxy.method1(null)); // Hello, CGLib!
System.out.println(proxy.hashCode()); // RuntimeException: Don not know what to do.
}
来看看设置MethodInterceptor接口的回调:
@Test
public void testMethodInterceptor() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProxyTarget.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello, CGLib!";
} else {
return proxy.invokeSuper(obj, args); // 执行原方法(被代理的方法)
}
}
});
ProxyTarget proxy = (ProxyTarget) enhancer.create();
System.out.println(proxy.method1(null)); // Hello, CGLib!
System.out.println(proxy.toString()); // ProxyTarget$ $EnhancerByCGLIB$$1de8b511@5d3411d
System.out.println(proxy.hashCode()); // 97730845
}
该接口中的interceptor接口接收四个参数:被代理对象、被代理的方法、传入方法的参数和方法代理(MethodProxy)。
其中invokeSuper()方法用来执行被代理对象上的方法,不会发生递归调用。
如果要通过代理对不同的方法执行不同的回调该怎么做呢?CGLib提供了CallbackFilter接口CallbackHelper(实现了 CallbackFilter接口)来达到这个目的:
@Test
public void testCallbackHelper() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProxyTarget.class);
CallbackHelper callbackHelper = new CallbackHelper(ProxyTarget.class, new Class[0]) {
@Override
protected Object getCallback(Method method) {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello, CGLib!";
}
};
} else {
return NoOp.INSTANCE;
}
}
};
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
ProxyTarget proxy = (ProxyTarget) enhancer.create();
System.out.println(proxy.method1(null)); // Hello, CGLib!
System.out.println(proxy.toString()); // ProxyTarget$ $EnhancerByCGLIB$$a66a4ff1@44e81672
}
@Test
public void testCallbackFilter() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProxyTarget.class);
Callback[] callbacks = {new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return "proxy - " + proxy.invokeSuper(obj, args);
}
}, new FixedValue() {
@Override
public Object loadObject() throws Exception {
return 100;
}
}, NoOp.INSTANCE};
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class)
return 0;
if (method.getReturnType() == Integer.class || method.getReturnType() == int.class)
return 1;
return 2;
}
});
enhancer.setCallbacks(callbacks);
ProxyTarget proxy = (ProxyTarget) enhancer.create();
System.out.println(proxy.method1("OK")); // proxy - OK
System.out.println(proxy.hashCode()); // 100
System.out.println(proxy.toString()); // ProxyTarget$ $EnhancerByCGLIB$$89c0dffe@64
}