简述
在上一篇博客中,我们仔细地分析了Java原生代理的实现机制,得出了如下结论
- 代理的类动态生成的。也就是说,在程序运行时创建类的字节码,并且动态加载到JVM中
- Java原生代理实际上是创造了一个实现了所有给定接口的类,并在调用接口的方法的时候使用反射达到代理效果
基于以上两点,我们能得出结论,如果要使用Java原生代理的话,就必须将类的每一个方法都写在接口里面再进行实现。而Cglib的出现,就解决了这个问题。在Cglib Github主页上我们能看到如此描述
Cglib(Code Generation Library) 能够提供生成和转换Java字节码的API,它被广泛运用在AOP,测试,数据处理以及生成动态代理和方法拦截的框架中。
接下来,我们看一下Cglib是如何工作的。
Cglib Proxy 示例
对于Cglib的代理对象,与Java原生代理对象相同,它们都需要生成一个代理类对代理对象进行持有,同时必须在生成这个代理类的时候,将一个回调方法处理类作为参数传入构造函数中。这样,当代理类的任意方法被调用的时候,Java原生代理会调用InvocationHandler
中的invoke
方法,而Cglib代理则会调用MethodInterceptor
中的intercept
方法。
仍然以前面博客中的UserDao
以及UserDaoImpl
为例进行代码示例。
SimpleMethodInterceptor.java
package com.study.cglib.learning;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class SimpleMethodInterceptor implements MethodInterceptor{
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("proxy control!");
//注意,如果使用invoke方法,并且参数也为代理类,则会循环进入此拦截方法从而导致爆栈
//invokeSuper即为调用原生的方法
return proxy.invokeSuper(obj, args);
}
public Object createProxy(Class<?> clazz, Class<?>[] interfaces) {
Enhancer enhancer = new Enhancer();
//设置需要代理的类
enhancer.setSuperclass(clazz);
//设置需要代理的接口
enhancer.setInterfaces(interfaces);
//设置处理方法的回调函数
enhancer.setCallback(this);
//设置回掉函数的过滤方法
enhancer.setCallbackFilter(new SimpleCallBackfilter());
return enhancer.create();
}
}
SimpleCallBackfilter.java
public class SimpleCallBackfilter implements CallbackFilter{
@Override
public int accept(Method method) {
System.out.println("filtering!");
return 0;
}
}
我们可以为代理类设置多个回调方法(将回调类以数组形式传入setCallBacks方法中),然后再设置callBackFilter,callBackFilter返回的整型代表着第几个回调类的拦截方法将会被调用。
App.java
package com.study.cglib.learning;
public class App
{
public static void main( String[] args )
{
SimpleMethodInterceptor cgProxy = new SimpleMethodInterceptor();
UserDao userDao = (UserDao) cgProxy.createProxy(UserDaoImpl.class, null);
userDao.queryName();
//输出 : filtering!
// filtering!
// filtering!
// filtering!
// filtering!
// filtering!
// proxy control!
// getUserName
}
}
浅析Cglib生成代理类的方式
Enhancer.class
通过继承AbstractClassGenerator
,并且实现了generateClass
来进行代理类的字节码动态生成,其方式和过程与Java原生代理类似,找到所有声明的方法,构建构造函数,构建代理函数,最后根据内存中生成的所有函数,拼凑成字节码。唯一不同的是,Cglib**直接继承**委托类。而CallBackFilter实际上也是在生成函数的时候生效的,如下所示。
Enhancer.emitMethods
while (it1.hasNext()) {
MethodInfo method = (MethodInfo)it1.next();
Method actualMethod = (it2 != null) ? (Method)it2.next() : null;
//通过callbackfilter确定目前的方法将会被哪个callback拦截
int index = filter.accept(actualMethod);
if (index >= callbackTypes.length) {
throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
}
...
}
Cglib之FastClass
与Java原生代理类似,我们也能够通过某个系统变量来保存Cglib产生的class文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");
但是,与原生代理类不同的是,如果生成了代理类并且进行了方法调用,将会生成三个class文件。
UserDaoImpl$$EnhancerByCGLIB$$c1115585$$FastClassByCGLIB$$d2f68b9b.class
UserDaoImpl$$EnhancerByCGLIB$$c1115585.class
UserDaoImpl$$FastClassByCGLIB$$e8aae1d6.class
如果只看文件名的话,发现两个是属于FastClass,而一个属于Enhancer,我们先观察Enhancer class。这次我们通过另外一个decompiler工具procyon来查看生成的文件
java -jar procyon-decompiler-0.5.30.jar UserDaoImpl$$EnhancerByCGLIB$$c1115585.class > UserDaoImpl$$EnhancerByCGLIB$$c1115585.class.java
//
// Decompiled by Procyon v0.5.30
//
package com.study.cglib.learning;
import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
public class UserDaoImpl$$EnhancerByCGLIB$$c1115585 extends UserDaoImpl implements Factory
{
//与Java原生代理类似,仍然以静态变量保存了指向代理方法的引用
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$queryName$0$Method;
private static final MethodProxy CGLIB$queryName$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
//每个函数有两个反射方法,一个是通过原生反射获得的,另外一个是通过Cglib构建的。
private static final Method CGLIB$hashCode$1$Method;
private static final MethodProxy CGLIB$hashCode$1$Proxy;
//...
//在类加载的时候,初始化所有静态变量
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
final Class<?> forName = Class.forName("com.study.cglib.learning.UserDaoImpl$$EnhancerByCGLIB$$c1115585");
final Class<?> forName2;
final Method[] methods = ReflectUtils.findMethods(new String[] { "hashCode", "()I", "equals", "(Ljava/lang/Object;)Z", "clone", "()Ljava/lang/Object;", "toString", "()Ljava/lang/String;", "finalize", "()V" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$hashCode$1$Method = methods[0];
//...初始化其它函数
}
//与原生java代理不同,针对委托类中的每个方法,在代理类中有两个实现,一个实现直接调用委托类方法,而另外一个则通过代理的回调进行调用。
final void CGLIB$queryName$0() {
super.queryName();
}
public final void queryName() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$queryName$0$Method, UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$emptyArgs, UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$queryName$0$Proxy);
return;
}
super.queryName();
}
//...clone, toString, equals, finalize方法
public static MethodProxy CGLIB$findMethodProxy(final Signature signature) {
final String string = signature.toString();
switch (string.hashCode()) {
case -1574182249: {
if (string.equals("finalize()V")) {
return UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$finalize$5$Proxy;
}
break;
}
//...
}
public UserDaoImpl$$EnhancerByCGLIB$$c1115585() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] value) {
UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$THREAD_CALLBACKS.set(value);
}
public static void CGLIB$SET_STATIC_CALLBACKS(final Callback[] cglib$STATIC_CALLBACKS) {
CGLIB$STATIC_CALLBACKS = cglib$STATIC_CALLBACKS;
}
private static final void CGLIB$BIND_CALLBACKS(final Object o) {
final UserDaoImpl$$EnhancerByCGLIB$$c1115585 userDaoImpl$$EnhancerByCGLIB$$c1115585 = (UserDaoImpl$$EnhancerByCGLIB$$c1115585)o;
if (!userDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$BOUND) {
userDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$BOUND = true;
Object o2;
if ((o2 = UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = UserDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$STATIC_CALLBACKS) != null) {
userDaoImpl$$EnhancerByCGLIB$$c1115585.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
}
}
}
//省略newInstance函数
public Callback getCallback(final int n) {
CGLIB$BIND_CALLBACKS(this);
Object cglib$CALLBACK_0 = null;
switch (n) {
case 0: {
cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0;
break;
}
default: {
cglib$CALLBACK_0 = null;
break;
}
}
return (Callback)cglib$CALLBACK_0;
}
public void setCallback(final int n, final Callback callback) {
switch (n) {
case 0: {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
break;
}
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[] { this.CGLIB$CALLBACK_0 };
}
public void setCallbacks(final Callback[] array) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)array[0];
}
static {
CGLIB$STATICHOOK1();
}
}
从上面可以看到,Cglib生成的代理对象,相比于Java原生的代理对象,会生成更多的函数以及更多的静态变量,因此,在生成代理对象的时候,可想而知Cglib会耗时更多。
看完了Enhancer生成的代理类,那么剩下的两个FastClass的类,又有什么作用呢?
在介绍这两个类之前,我们先回顾一下前面提到的MethodInterceptor。
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
再深究一下,methody proxy的invokerSuper
和invoke
方法
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
不难发现,两者都引用了FastClassInfo
。而实际上,两个FastClass正是因此而产生的。如果只生成代理对象,不通过代理对象调用方法的话,只会生成一个class文件,而一旦通过代理对象调用方法,就会生成三个class文件。
两个FastClass文件,是为了减少反射调用的时间,将反射调用转化为直接调用。
UserDaoImpl$$FastClassByCGLIB$$e8aae1d6.class
package com.study.cglib.learning;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
public class UserDaoImpl$$FastClassByCGLIB$$e8aae1d6 extends FastClass
{
public UserDaoImpl$$FastClassByCGLIB$$e8aae1d6(final Class clazz) {
super(clazz);
}
public int getIndex(final Signature signature) {
final String string = signature.toString();
switch (string.hashCode()) {
case -1725733088: {
if (string.equals("getClass()Ljava/lang/Class;")) {
return 1;
}
break;
}
//其它函数的处理分支
}
return -1;
}
public int getIndex(final String s, final Class[] array) {
Label_0438: {
switch (s.hashCode()) {
case -1807064205: {
if (!s.equals("queryName")) {
break;
}
switch (array.length) {
case 0: {
return 0;
}
default: {
break Label_0438;
}
}
break;
}
//其它函数的处理分支
}
}
return -1;
}
public int getIndex(final Class[] array) {
switch (array.length) {
case 0: {
return 0;
}
default: {
return -1;
}
}
}
public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
final UserDaoImpl userDaoImpl = (UserDaoImpl)o;
try {
switch (n) {
case 0: {
userDaoImpl.queryName();
return null;
}
//其它函数的处理分支
}
}
catch (Throwable target) {
throw new InvocationTargetException(target);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object newInstance(final int n, final Object[] array) throws InvocationTargetException {
try {
switch (n) {
case 0: {
return new UserDaoImpl();
}
}
}
catch (Throwable target) {
throw new InvocationTargetException(target);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public int getMaxIndex() {
return 9;
}
}
UserDaoImpl$$FastClassByCGLIB$$e8aae1d6
实际上是上面invoke
方法中使用的fastclass。而另外一个UserDaoImpl$$EnhancerByCGLIB$$c1115585$$FastClassByCGLIB$$d2f68b9b
则是在invokeSuper
中使用的fastclass。fastclass将反射调用转化成了直接调用。
小结
以上,就是Cglib中较为简单的部分,我们在这篇文章里面完成了Cglib生成动态代理的简单示例,为代理设置了回调类以及过滤类,并且分析了Cglib在生成动态代理、调用方法时会生成的两种不同类型的文件。
我们可以简略得出以下结论
- Cglib代理的方式是直接继承委托类,因而无法代理final类
- 在生成代理类的过程中,会生成两个方法,一个方法直接调用委托类中的对应方法,另一个方法则覆盖了委托类的对应方法,由此可知,Cglib生成类的速度要慢于Java原生代理,而且final方法也无法被代理。
- 除了生成代理类,Cglib还会生成相应的FastClass作为方法调用的索引,以减少反射调用所消耗的时间。