一、背景
在上一篇博客中(java动态代理:http://blog.youkuaiyun.com/wenhuayuzhihui/article/details/51700670)提到,java的动态代理是必须基于接口的,而在编程中,使用到的外部类并不是基于接口编程的比比皆是,这有如何实现动态代理?使用可以基于类实现动态代理的CGLib!
二、简介
CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口;它的底层是使用java字节码操作框架ASM实现;
三、使用
CGLib核心类:
1、net.sf.cglib.proxy.Enhancer:主要增强类,通过字节码技术动态创建委托类的子类实例;
2、net.sf.cglib.proxy.MethodInterceptor:常用的方法拦截器接口,需要实现intercept方法,实现具体拦截处理;
public java.lang.Object intercept(java.lang.Object obj,
java.lang.reflect.Method method,
java.lang.Object[] args,
MethodProxy proxy)
throws java.lang.Throwable
obj:动态生成的代理对象
method : 实际调用的方法
args:调用方法入参
proxy:
net.sf.cglib.proxy.MethodProxy:java Method类的代理类,可以实现委托类对象的方法的调用;常用方法:methodProxy.invokeSuper(proxy, args);在拦截方法内可以调用多次
我们继续以java动态代理实例的需求演示CGLib动态代理(调用外部类方法是,打印入参,完成后,打印结果),假设外部类没有基于接口编程,我们无法再使用java的动态代理实现这个需求,只能使用CGLib实现:
/**
* 简单的外部类
* @author hwz
*
*/
public class OtherOuterClass {
/**
* 字符串翻转
* @param str
* @return
*/
public String methodOne(String str) {
return new StringBuilder(str).reverse().toString();
}
/**
* 两个整数乘积
* @param a
* @param b
* @return
*/
public int methodSecond(int a, int b) {
return a*b;
}
}
/**
* 代理方法拦截器
* @author hwz
*
*/
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//打印入参日志
StringBuilder builder = new StringBuilder();
if (args != null) {
for (int i=0; i<args.length; ++i) {
builder.append(",arg" + i + "=" + args[i].toString());
}
}
System.out.println("Enter " + method.toString() + ",args:" + builder.toString());
//Object result = method.invoke(obj, args); //直接使用obj调用方法,会发生和java动态代理一样的无限循环调用
Object result = proxy.invokeSuper(obj, args);
//打印结果日志
System.out.println("Leave " + method.toString() + ",result=" + result.toString());
return result;
}
}
/**
* 测试类
* @author hwz
*
*/
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
OtherOuterClass proxyObj = (OtherOuterClass) cglibProxy.getProxy(OtherOuterClass.class);
proxyObj.methodOne("abcdef");
proxyObj.methodSecond(2, 4);
}
}
在spring的AOP中,就是使用到java的动态代理和CGLib,默认情况下,基于接口的切面是使用java动态代理;
有比较表明CGLib的性能高于java本身的动态代理,后续我们再比较分析;