JDK动态代理是基于接口的,代理类必须实现相同的接口,然而这么做有时候感觉有点不爽啊,要改来改去,总归有点不方便。那么,有没有不需要实现相同接口就能实现动态代理的东西呢。Java作为一种字节码的解释性语言,怎么可能没有这玩意。没错,文章标题的Cglib动态代理就实现了这种功能,当然了,这玩意一样是基于jvm的,在运行期弄的。没办法,java在编译期就想弄出来,着实有点搞不过来。
首先,不着急,写个简单的用例先。
/**
* 受代理的目标class
*/
class Hello {
public void caca() {
System.out.println("caca");
}
public void say() {
System.out.println("say");
}
}
/**
* 实现MethodInterceptor 接口的代理class
*/
class CglibProxy implements MethodInterceptor {
public void before() {
System.out.println("before----------");
}
public void after() {
System.out.println("after----------");
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
before();
methodProxy.invokeSuper(obj, args);
after();
return null;
}
}
/**
* main方法入口
*/
public static void main(String[] args) {
//调用Enhancer的create方法,生成代理
Hello hello = (Hello)Enhancer.create(Hello.class, new CglibProxy());
hello.say();
}
输出结果:
before----------
say
after----------
就那么短短几行,就生成了代理,是不是觉得很简单。而且,我不管怎么改动Hello类,其代理接口都不需要变动,着实比JDK动态代理强多了,其耦合性大大减少。说笑了,JDK 动态代理和Cglib动态代理各有优劣,都有合适的场景。
下面看一看该Cglib代理的生成流程吧。
为了更好地说明create方法的内部原理,我把create内部的方法给反射出来调用了。没错,你没听错,我反射出来调用了。原本想要放源码包Debug的,但是一时神经,就反射出来调用了,没想到调用起来这么麻烦。
下面就先贴上我傻乎乎反射调用写出来的代码。
/**
* 反射调用工具类,看名字不像,别慌,随便起的
*/
class ClassObj {
private Object object;
private Class<?> objClass;
public ClassObj(Object object) {
this.object = object;
this.objClass = object.getClass();
}
/**
* 通过获取方法名和参数类型获取方法
*/
public Method getMethod(String methodName, Class<?>... args)
throws NoSuchMethodException, SecurityException {
//为图方便直接复制粘贴了三遍try...catch,测试可以,实际可别这么做
try {
return objClass.getDeclaredMethod(methodName, args);
} catch (Exception e1) {
try {
return objClass.getSuperclass().getDeclaredMethod(methodName,
args);
} catch (Exception e2) {
try {
return objClass.getSuperclass().getSuperclass()
.getDeclaredMethod(methodName, args);
} catch (Exception e3) {
return objClass.getSuperclass().getSuperclass()
.getSuperclass().getSuperclass()
.getDeclaredMethod(methodName, args);
}
}
}
}
/**
* 获取并调用方法
*/
@SuppressWarnings("rawtypes")
public Object invokeMethod(String methodName, Class[] classes,
Object... args) throws NoSuchMethodException, SecurityException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
//setAccessible放入getMethod比较合适,我只是随便写,勿仿这风格
Method method = getMethod(methodName, classes);
method.setAccessible(true);
return method.invoke(object, args);
}
/**
* 获取并调用方法
*/
public Object invokeMethod(String methodName) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
return invokeMethod(methodName, new Class[] {}, new Object[] {});
}
/**
* 获取域属性
*/
public Field getFiled(String fieldName) throws NoSuchFieldException,
SecurityException {
//为图方便直接复制粘贴了三遍try...catch,测试可以,实际可别这么做
try {
return objClass.getDeclaredField(fieldName);
} catch (Exception e1) {
try {
return objClass.getSuperclass().getDeclaredField(fieldName);
} catch (Exception e2) {
return objClass.getSuperclass().getSuperclass()
.getDeclaredField(fieldName);
}
}
}
/*
* 获取域值
*/
public Object getValue(String fieldName) throws IllegalArgumentException,
IllegalAccessException, NoSuchFieldException, SecurityException {
//setAccessible放入getFiled比较合适,我只是随便写,勿仿这风格
Field field = getFiled(fieldName);
field.setAccessible(true);
return field.get(object);
}
/*
* 设置域值
*/
public void setValue(String fieldName, Object obj)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException {
//setAccessible放入getFiled比较合适,我只是随便写,勿仿这风格
Field field = getFiled(fieldName);
field.setAccessible(true);
field.set(this.object, obj);
}
}
下面贴上我将Enhancer.create里面挖出来的main方法,我将Enhancer.create方法以反射的形式写在main方法当中,当然了,限于篇幅,这里只贴上反射后的主逻辑的代码,其它什么缓存啊,线程隔离啊等等等等的代码我全删掉了。
public static void main(String[] args) throws Exception {
//hello最后将会被用来指向生成的代理对象
//初始化Enhancer,并生成我们自己的ClassObj对象,利用该对象完成对Enhancer对象的操纵
//字节数组b后面将会被用来存放生成的未加载的Class的信息
//gen后面将用来存放加载后的代理对象的class
//cache2是一个HashMap,后面过程中进行缓存的
Hello hello = null;
Enhancer e = new Enhancer();
ClassObj eo = new ClassObj(e);
byte[] b;
Class gen = null;
Map cache2 = new HashMap();
/*
* 这里设置一些基本信息
* 并且将我们的Hello.class这个class信息对象传入
* 这时候,Enhancer就知道了所要代理的class信息
*/
e.setSuperclass(Hello.class);
e.setCallback(new CglibProxy());
eo.setValue("classOnly", false);
eo.setValue("argumentTypes", null);
eo.invokeMethod("validate");
eo.invokeMethod("setNamePrefix", new Class[] { String.class },
((Class) eo.getValue("superclass")).getName());
/**
* 获取EnhancerKey的实现对象,这个生成看似很长,但是别慌
* 也就是利用一些属性生成一个Key而已
*/
Object key = ((EnhancerKey) eo.getValue("KEY_FACTORY")).newInstance(
((Class) eo.getValue("superclass")).getName(),
ReflectUtils.getNames(((Class[]) eo.getValue("interfaces"))),
(CallbackFilter) eo.getValue("filter"),
(Type[]) eo.getValue("callbackTypes"),
(boolean) eo.getValue("useFactory"),
(boolean) eo.getValue("interceptDuringConstruction"),
(Long) eo.getValue("serialVersionUID"));
synchronized (eo.getValue("source")) {
//获取类加载器
//该ClassLoader被用做key,在生成class的字节数组通过该key获取相关信息来生成字节码
ClassLoader loader = (ClassLoader) eo
.invokeMethod("getClassLoader");
ClassObj source = new ClassObj(eo.getValue("source"));
Map cache = (Map) source.getValue("cache");
cache2.put(eo.getValue("NAME_KEY"), new HashSet());
cache.put(loader, cache2);
if (gen == null) {
//设置键
//生成字节数组b的时候需要用到
eo.setValue("key", key);
if (gen == null) {
//将我们的传入的代理对象的信息转成字节码存入byte[]数组里面
//可以将字节数组b的值写入到文件当中,然后反编译可以看到生成的代理的代码信息
GeneratorStrategy strategy = (GeneratorStrategy) eo
.getValue("strategy");
b = strategy.generate(e);
//读取字节码里的className信息
String className = ClassNameReader
.getClassName(new ClassReader(b));
//加载字节数组b里存的class信息
//这里生成目标代理的class
gen = ReflectUtils.defineClass(className, b, loader);
}
//实例化代理对象
hello = (Hello) eo.invokeMethod("firstInstance",
new Class[] { Class.class }, gen);
//当然了,利用下面也可以实例化,删除了一些细节,因此仅限于本例
//hello = (Hello) gen.newInstance();
//Method method = gen.getDeclaredMethod("setCallbacks",Callback[].class);
//method.invoke(hello,new Object[] { new Callback[] { new CglibProxy() } });
//最后输出验证结果
hello.say();
}
}
}
}
输出结果如下,一切正常
before----------
say
after----------
下面是反编译代理类后的基本信息
public class com.my.other.Hello$$EnhancerByCGLIB$$65187392 extends com.my.other.Hello implements net.sf.cglib.proxy.Factory {
private boolean CGLIB$BOUND;
private static final java.lang.ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final net.sf.cglib.proxy.Callback[] CGLIB$STATIC_CALLBACKS;
private net.sf.cglib.proxy.MethodInterceptor CGLIB$CALLBACK_0;
private static final java.lang.reflect.Method CGLIB$say$0$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$say$0$Proxy;
private static final java.lang.Object[] CGLIB$emptyArgs;
private static final java.lang.reflect.Method CGLIB$caca$1$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$caca$1$Proxy;
private static final java.lang.reflect.Method CGLIB$finalize$2$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$finalize$2$Proxy;
private static final java.lang.reflect.Method CGLIB$equals$3$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$equals$3$Proxy;
private static final java.lang.reflect.Method CGLIB$toString$4$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$toString$4$Proxy;
private static final java.lang.reflect.Method CGLIB$hashCode$5$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$hashCode$5$Proxy;
private static final java.lang.reflect.Method CGLIB$clone$6$Method;
private static final net.sf.cglib.proxy.MethodProxy CGLIB$clone$6$Proxy;
static void CGLIB$STATICHOOK1();
final void CGLIB$say$0();
public final void say();
final void CGLIB$caca$1();
public final void caca();
final void CGLIB$finalize$2() throws java.lang.Throwable;
protected final void finalize() throws java.lang.Throwable;
final boolean CGLIB$equals$3(java.lang.Object);
public final boolean equals(java.lang.Object);
final java.lang.String CGLIB$toString$4();
public final java.lang.String toString();
final int CGLIB$hashCode$5();
public final int hashCode();
final java.lang.Object CGLIB$clone$6() throws java.lang.CloneNotSupportedException;
protected final java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public static net.sf.cglib.proxy.MethodProxy CGLIB$findMethodProxy(net.sf.cglib.core.Signature);
public com.my.other.Hello$$EnhancerByCGLIB$$65187392();
public static void CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]);
public static void CGLIB$SET_STATIC_CALLBACKS(net.sf.cglib.proxy.Callback[]);
private static final void CGLIB$BIND_CALLBACKS(java.lang.Object);
public java.lang.Object newInstance(net.sf.cglib.proxy.Callback[]);
public java.lang.Object newInstance(net.sf.cglib.proxy.Callback);
public java.lang.Object newInstance(java.lang.Class[], java.lang.Object[], net.sf.cglib.proxy.Callback[]);
public net.sf.cglib.proxy.Callback getCallback(int);
public void setCallback(int, net.sf.cglib.proxy.Callback);
public net.sf.cglib.proxy.Callback[] getCallbacks();
public void setCallbacks(net.sf.cglib.proxy.Callback[]);
static {};
}
从上面可以看出,该class的方法和成员贼多,至少比JDK的动态代理多很多,因此,用Cglib初始化代理相对JDK动态代理,在初始化对象的时候,慢很多。不过对象出来后就没什么了。所以Cglib比较适合初始化的阶段使用。附上生成代理对象的class样例文件http://download.csdn.net/detail/qwe125698420/9629960 额外话:另外大家有什么好的反编译器可以推荐下,用的几款反编译工具最近反编译老失败。