Java Cglib动态代理原理源码分析

Posted Springboot实战案例锦集

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java Cglib动态代理原理源码分析相关的知识,希望对你有一定的参考价值。

环境:Java8


Cglib代理使用

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib");  
Enhancer enhancer = new Enhancer() ;
enhancer.setSuperclass(PersonDAOImpl.class) ;
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("执行前...") ;
Object result = proxy.invokeSuper(obj, args) ;
System.out.println("执行后...") ;
return result ;
}
});
PersonDAOImpl dao = (PersonDAOImpl) enhancer.create() ;
dao.save(new Person()) ;

System.setProperty(
DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib");这行代码用来生成cglib生成的代理类,输出到指定目录


反编译代理类

工具使用的是Luyten

由于代码量比较大,这里只贴出重要的代码

public class PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7 extends PersonDAOImpl implements Factory
{
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$save$0$Method;
private static final MethodProxy CGLIB$save$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;

static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7");
final Class<?> forName2;
final Method[] methods = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = methods[0];
CGLIB$equals$1$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = methods[1];
CGLIB$toString$2$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = methods[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = methods[3];
CGLIB$clone$4$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
final Class<?> forName3;
CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] { "save", "(Lcom/pack/anno/config/Person;)V" }, (forName3 = Class.forName("com.pack.dao.PersonDAOImpl")).getDeclaredMethods())[0];
CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");
}

final void CGLIB$save$0(final Person person) {
super.save(person);
}

public final void save(final Person person) {
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, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Method, new Object[] { person }, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Proxy);
return;
}
super.save(person);
}

public PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7() {
CGLIB$BIND_CALLBACKS(this);
}

public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] array) {
PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$THREAD_CALLBACKS.set(array);
}
private static final void CGLIB$BIND_CALLBACKS(final Object o) {
final PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7 personDAOImpl$$EnhancerByCGLIB$$6f07c3f7 = (PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7)o;
if (!personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$BOUND) {
personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$BOUND = true;
Object o2;
if ((o2 = PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$STATIC_CALLBACKS) != null) {
personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
}
}
}

static {
CGLIB$STATICHOOK1();
}
}

生成的代理类继承了PersonDAOImpl;PersonDAOImpl中有几个方法就会生成对应的两个方法,一个方法是直接调用父类对象方法CGLIB$save$0,一个是save方法而这个save方法是通过Callback来调用

当在执行save方法的时候先判断CGLIB$CALLBACK_0是否为空,如果为空则会执行初始化方法

CGLIB$BIND_CALLBACKS(this);

在该方法中,会从CGLIB$THREAD_CALLBACKS对象(ThreadLocal)中获取回调方法(Callback),这个ThreadLocal对象是如何设置值的?

设置CGLIB$THREAD_CALLBACKS值

在执行enhancer.create()方法时会通过反射来调用代理类PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7的CGLIB$SET_THREAD_CALLBACKS方法

在创建代理类的时候时:

public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
setThreadCallbacks(callbacks);
try {
// Explicit reference equality is added here just in case Arrays.equals does not have one
if (primaryConstructorArgTypes == argumentTypes ||
Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
// If we have relevant Constructor instance at hand, just call it
// This skips "get constructors" machinery
return ReflectUtils.newInstance(primaryConstructor, arguments);
}
// Take a slow path if observing unexpected argument types
return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
} finally {
// clear thread callbacks to allow them to be gc'd
setThreadCallbacks(null);
}

}

setThreadCallbacks(callbacks);这行代码就是用来执行代理类中的CGLIB$SET_THREAD_CALLBACKS方法。

private void setThreadCallbacks(Callback[] callbacks) {
try {
setThreadCallbacks.invoke(generatedClass, (Object) callbacks);
} catch (IllegalAccessException e) {
throw new CodeGenerationException(e);
} catch (InvocationTargetException e) {
throw new CodeGenerationException(e.getTargetException());
}
}

Java Cglib动态代理原理源码分析


在这代理类中的MethodInterceptor 就设置完成,接下来会执行代理类中save方法的如下代码:

cglib$CALLBACK_2.intercept((Object)this, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Method, new Object[] { person }, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Proxy);

MethodInterceptor中intercept方法的执行

intercept方法

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("执行前...") ;
Object result = proxy.invokeSuper(obj, args) ;
System.out.println("执行后...") ;
return result ;
}

这里的MethodProxy proxy对象又是从哪里来的?这需要回到代理类中的静态代码段中的如下一行代码:

CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");

进入MethodProxy.create方法:

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}

进入invokeSuper方法

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();
}
}

init方法用来初始化一个fastClassInfo对象

private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;

FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
// FastClassInfo
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
}

f1为对应的是CreateInfo中的c1,f2对应的是CreateInfo中的c2;这里结合代理类中的静态代码段来看看分别执行的是谁

final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7");
final Class<?> forName3;
CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] { "save", "(Lcom/pack/anno/config/Person;)V" }, (forName3 = Class.forName("com.pack.dao.PersonDAOImpl")).getDeclaredMethods())[0];
CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");

结合上面的代码已经很清楚了。

f1表示的目标类的快速访问类,f2表示的代理类的快速访问类;i1表示的是目标类方法对应的索引,i2表示的是代理类方法对应的索引。

快速访问类是继承了FastClass类,其目的就是能快速地调用执行,因为通过反射性能不高。

原理就是通过每个方法的签名信息(方法名,参数)来为每个方法生成一个索引值,然后调用方法的时候就根据这个索引值直接调用目标方法。

目标类的快速访问类

Java Cglib动态代理原理源码分析


继续回到invokeSuper方法执行

FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);

此时fci对象完整信息如下:

Java Cglib动态代理原理源码分析


fci.f2.invoke(fci.i2, obj, args);这行代码才是真正执行目标类的代码,注意这里的i2=19

进入到PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7$$FastClassByCGLIB$$8343b7a5.class中查看invoke方法

Java Cglib动态代理原理源码分析


PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7类中的CGLIB$save$0方法

Java Cglib动态代理原理源码分析


直接调用父类PersonDAOImpl中的save方法。

到这里也就明白cglib生成的3个class。


完毕!!!

给个关注+转发呗谢谢

公众:Springboot实战案例锦集


以上是关于Java Cglib动态代理原理源码分析的主要内容,如果未能解决你的问题,请参考以下文章

javaJDK动态代理源码分析 到生成字节码

设计模式----代理模式

jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

保姆级手把手捋动态代理流程(JDK+Cglib超详细源码分析)

cglib测试例子和源码详解

动态代理—— CGLIB代理原理