jdk及cglib动态代理原理
YoutiaoGuagua Lv1

jdk及cglib动态代理原理

环境

image

  • 日志框架logback,引入logback-classic即可引入日志实现层和api

  • 测试框架junit,引入junit-jupiter即可,这是一个聚合pom

  • 引入cglig库

原理分析

java动态代理有两种实现方式:

  • JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

  • CGLIB动态代理:利用asm,修改字节码生成子类来处理。

jdk动态代理

代理方法执行分析

具体实现如下,使用jdk自带的Proxy实现InvocationHandler接口即可对代理对象进行增强

1
2
3
4
5
6
7
8
9
HelloWorldImpl o = new HelloWorldImpl();
HelloWorld helloWorld = (HelloWorld) Proxy.newProxyInstance(HelloWorld.class.getClassLoader(), HelloWorldImpl.class.getInterfaces(), (obj, method, args) -> {
log.info("调用方法开始:{}", method.getName());
Object result = method.invoke(o, args);
log.info("调用结果:{}", result);
log.info("调用方法结束:{}", method.getName());
return result;
});
log.info(helloWorld.sayHello("hello world"));

通过设置以下代码可以查看jdk动态生成的class文件

1
2
//  设置java动态代理调试模式,在junit中设置此方法是无效的!
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

下面为生成的代理类

1
2
3
4
├───com
│ └───sun
│ └───proxy
│ $Proxy0.class # 生成的代理类
1
2
3
4
5
6
7
8
9
public final String sayHello(String var1) throws  {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

上面为生成的的class文件反编译后的一个方法,很容易的看出,代理方法只是很简单的调用了InvocationHandler类中的invoke方法,在invoke方法中又会调用代理方法的invoke方法,这个invoke方法是反射方法,因此被代理的方法就被调用了,因此jdk动态代理是通过实现被代理类的接口,然后通过反射执行被代理方法实现的。

生成代理类分析

下面咱们来看一下这个动态代理类是怎么生成的

下面为Proxy.newProxyInstance的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
....
}

很容易看出getProxyClass0就是生成代理类的方法。

1
2
3
4
5
6
7
8
9
10
11
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}

比较有意思的一点是当被代理类的接口数量大于65535时就会报错,我们都知道一个类可以实现多个接口,但是这个接口数量也是有限制的,挺有意思的😂!

代理类是被放在一个弱引用map里的,接下来就是从map中取出接口的代理类。

1
2
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

上面就是这个map构造方式,提供了key和value的构造工厂。

KeyFactory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//很简单的通过实现类的数量生成了一个Key对象    
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}

ProxyClassFactory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 代理类的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
//验证类加载器是否将此接口的名称解析为相同的 Class 对象,简单点说就是这个接口是否可以被提供的类加载器加载。
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
// 确认是否是个接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
//验证接口是否是重复的
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 看注释挺绕的,应该就是想设置一个包名
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}

// 设置代理类类名称
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

//生成代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}

通过上面我们可以很容易的了解代理类生成的过程了,ProxyGenerator.generateProxyClass应该是最重要的了,但是没必要深究了,无非就是生成方法,生成构造器等。另外上面提到在junit中无法通过设置变量生成代理类class文件,我们可以直接调用ProxyGenerator.generateProxyClass手动生成。

Cglib动态代理分析

代理方法执行分析

同jdk动态代理一样,首先要打开cglib的debug模式才能看见代理类

1
2
//  设置Cglib动态代理调试模式
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target");

写一个最简单的使用方式:

1
2
3
4
5
6
7
8
9
10
11
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWorldImpl.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {
log.info("调用方法开始:{}", method.getName());
Object result = methodProxy.invokeSuper(obj, args);
log.info("调用结果:{}", result);
log.info("调用方法结束:{}", method.getName());
return result;
});
HelloWorld helloWorld = (HelloWorld) enhancer.create();
log.info(helloWorld.sayHello("cglib"));

运行之后在target中看一下生成的代理类:

1
2
3
4
5
6
7
8
├───com
│ └───example
│ └───cglib
│ └───proxy
│ └───impl
│ HelloWorldImpl$$EnhancerByCGLIB$$722f73d4$$FastClassByCGLIB$$5d1e80b1.class
│ HelloWorldImpl$$EnhancerByCGLIB$$722f73d4.class
│ HelloWorldImpl$$FastClassByCGLIB$$d5c59250.class

可以看见生成了三个类,下面来看一下这三个类都有什么吧,ヾ(≧▽≦*)o

  1. HelloWorldImpl$$EnhancerByCGLIB$$722f73d4

    1
    2
    3
    4
    5
    6
    7
    public class HelloWorldImpl$$EnhancerByCGLIB$$722f73d4 extends HelloWorldImpl implements Factory {
    //省略大部分代码
    final String CGLIB$sayHello$1(String var1) {}

    public final String sayHello(String var1) {}
    //省略大部分代码
    }

    这个类类似于jdk动态代理生成的那个代理类,之后我们来看他是怎么执行的。

  2. HelloWorldImpl$$EnhancerByCGLIB$$722f73d4$$FastClassByCGLIB$$5d1e80b1.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class HelloWorldImpl$$EnhancerByCGLIB$$722f73d4$$FastClassByCGLIB$$5d1e80b1 extends FastClass {
    public HelloWorldImpl$$EnhancerByCGLIB$$722f73d4$$FastClassByCGLIB$$5d1e80b1(Class var1) {}

    public int getIndex(Signature var1) {}

    public int getIndex(String var1, Class[] var2) {}

    public int getIndex(Class[] var1) {}

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {}

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {}

    public int getMaxIndex() {}
    }

    这个类里最重要的就是getIndex方法和invoke方法。

  3. HelloWorldImpl$$FastClassByCGLIB$$d5c59250.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class HelloWorldImpl$$FastClassByCGLIB$$d5c59250 extends FastClass {
    public HelloWorldImpl$$FastClassByCGLIB$$d5c59250(Class var1) {}

    public int getIndex(Signature var1) {}

    public int getIndex(String var1, Class[] var2) {}

    public int getIndex(Class[] var1) {}

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {}

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {}

    public int getMaxIndex() {}
    }

    和上一个类相似,但是有本质区别。

下面将对上面的三个类进行分析

先不分析enhancer.create方法,首先上面的例子会调用HelloWorld.sayHello方法,然后调用代理类的sayHello方法。

1
2
3
4
5
6
7
8
9
public final String sayHello(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}

return var10000 != null ? (String)var10000.intercept(this, CGLIB$sayHello$1$Method, new Object[]{var1}, CGLIB$sayHello$1$Proxy) : super.sayHello(var1);
}

可以看见sayHello方法会去掉用上面设置的回调方法,然后就进入了我们自己写的callBack方法,callback方法的一个继承接口是MethodInterceptor

1
2
3
4
public interface MethodInterceptor extends Callback
{
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}

可以看见这个intercept方法有四个参数:

  • obj是代理类
  • method是被代理类的方法
  • args是方法传入的参数
  • proxy是代理类的方法

之后肯定是执行方法了,首先可以想到的是直接调用method.invoke方法,这个其实就是jdk动态代理执行方法的方式,使用反射执行,这样就没cglib的优势了。值得注意的是invoke的第一个参数不要直接传入参的obj,这样会造成死循环,至于为什么会这样后面会解释。

不使用method.invoke执行方法,那么只能使用proxy去执行方法了,问题又来了,这个proxy有两个方法invokeinvokeSuper方法,那么这两个方法有什么区别呢?

首先来看invokeSuper

1
2
3
4
5
6
7
8
9
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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;
}
}
}
}

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;
}
1
2
3
4
5
6
7
//HelloWorldImpl$$EnhancerByCGLIB$$722f73d4静态代码块
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
CGLIB$sayHello$4$Method = var10000[4];
CGLIB$sayHello$4$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "sayHello", "CGLIB$sayHello$4");
}

可以看见invokeSuper首先调用了init方法,init方法需要一个createInfo这个createInfo方法是由create方法创建的,那么这个create是由什么调用的呢,通过debug可以看见是由我们的HelloWorldImpl代理类在静态代码块中调用的,可以看见这个代码块传递了方法参数,和代理类中的两个方法,查看这两个方法可以看出sayHello会调用callbackCGLIB$sayHello$4会调用被代理类的sayHello方法。

得到ci类之后之后会调用helper方法,这个方法主要就是生成两个fastClass类,通过fastClass.getIndex可以很快的得到执行的方法。而f2其实就是被代理类的执行方法,因此可以看出fci.f2.invoke其实执行的就是CGLIB$sayHello$4方法。从头到尾没有使用反射执行方法,简单的通过super.sayHelloWorld就完成了方法的执行。这也是cglib比jdk动态代理快的奥秘。

下面我们look lookproxy.invoke方法

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
}

可以看见和invokeSuper几乎一样,只是最后调用的是fci.f1.invoke方法,在invokeSuper中我们传入的obj参数就是MethodInterceptor#intercept所传给我们的obj,如果在invoke也传入这个obj的话,那么将会进入死循环,为什么会出现死循环呢,从上面的init方法中我们可以得出f1其实执行的是sayHello方法,而代理对象的sayHello方法会接着执行MethodInterceptor#intercept方法,然后MethodInterceptor#intercept会接着执行invoke方法,从而陷入死循环,解决这个问题很简单,就是自己new一个HelloWorldImpl对象传入,这样执行的就是你传入的这个对象的sayHello方法而不是代理对象的。上面提到的如果你执行method.invoke传入参数的obj会陷入死循环是一个道理,因为执行的是代理类的sayHello,而代理类的sayHello会接着执行callback

生成代理类分析

下面我们来see see enhancer.create干了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}

private Object createHelper() {
preValidate();
// 这个key其实就是个上下文,后面可以根据这个key创建代理类
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
1
2
3
4
5
6
7
8
9
10
protected Object create(Object key) {
...
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
...
}

data.get将会生成class文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public V get(K key) {
final KK cacheKey = keyMapper.apply(key);
Object v = map.get(cacheKey);
// 从map里找,如果有的话就不会构建class文件了
if (v != null && !(v instanceof FutureTask)) {
return (V) v;
}

return createEntry(key, cacheKey, v);
}

protected V createEntry(final K key, KK cacheKey, Object v) {
FutureTask<V> task;
boolean creator = false;
if (v != null) {
//如果有别的线程已经执行了,那么就不创建新线程了
task = (FutureTask<V>) v;
} else {
task = new FutureTask<V>(new Callable<V>() {
public V call() throws Exception {
return loader.apply(key);
}
});
Object prevTask = map.putIfAbsent(cacheKey, task);
// 查看map中是否已经存在任务了
if (prevTask == null) {
creator = true;
task.run();
} else if (prevTask instanceof FutureTask) {
task = (FutureTask<V>) prevTask;
} else {
return (V) prevTask;
}
}


V result;
try {
result = task.get();
} catch (InterruptedException e) {
throw new IllegalStateException("Interrupted while loading cache item", e);
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw ((RuntimeException) cause);
}
throw new IllegalStateException("Unable to load cache item", cause);
}
if (creator) {
map.put(cacheKey, result);
}
return result;
}

loader.apply将会生成代理,调用net.sf.cglib.proxy.Enhancer#generateClass构造org.objectweb.asm.ClassVisitor,之后生成代理类,里头的逻辑比较多,就不一一分析了,主要是使用了asm库构造新类。

Cglib的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@DisplayName("不同的回调实现")
@Nested
@Tag("cglib")
class DifferentCallback {

@Test
@DisplayName("为每个方法设置回调")
public void testMultiCallBack() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWorldImpl.class);
enhancer.setCallbackFilter(new CglibFilter());
enhancer.setCallbacks(new MethodInterceptor[]{new AuthInterceptor(), new NormalInterceptor()});
HelloWorld helloWorld = (HelloWorld) enhancer.create();
log.info("最终返回结果:{}", helloWorld.sayHello("cglib"));
log.info("最终返回结果:{}", helloWorld.sayGoodBye("cglib"));
}

@Test
@DisplayName("FixedValue")
public void testFixedValue() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWorldImpl.class);
enhancer.setCallback((FixedValue) () -> "fixedValue");
HelloWorld helloWorld = (HelloWorld) enhancer.create();
log.info("最终返回结果:{}", helloWorld.sayHello("cglib"));
}

@Test
@DisplayName("NoOp")
public void testNoOp() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWorldImpl.class);
enhancer.setCallback(NoOp.INSTANCE);
HelloWorld helloWorld = (HelloWorld) enhancer.create();
log.info("最终返回结果:{}", helloWorld.sayHello("cglib"));
}

@Test
@DisplayName("LazyLoader")
public void testLazyLoader() {
HelloWorldImpl helloWorld = new HelloWorldImpl();
HelloWorldImpl helloWorldLazy = helloWorld.lazyLoad();
log.info("开始调用getField方法....");
log.info("lazyLoad:{}", helloWorldLazy.getField());
log.info("lazyLoad:{}", helloWorldLazy.getField());
}


@Test
@DisplayName("Dispatcher")
public void testDispatcher() {
HelloWorldImpl helloWorld = new HelloWorldImpl();
HelloWorldImpl helloWorldDispatcher = helloWorld.dispatcher();
log.info("开始调用getField方法....");
log.info("dispatcher:{}", helloWorldDispatcher.getField());
log.info("dispatcher:{}", helloWorldDispatcher.getField());
}

@Test
@DisplayName("ProxyRefDispatcher")
public void testLazyLoader2() {
HelloWorldImpl helloWorld = new HelloWorldImpl();
HelloWorldImpl helloWorldProxyRefDispatcher = helloWorld.proxyRefDispatcher();
log.info("开始调用getField方法....");
log.info("lazyLoad:{}", helloWorldProxyRefDispatcher.getField());
log.info("lazyLoad:{}", helloWorldProxyRefDispatcher.getField());
}
}

以上代码获取

 评论
评论插件加载失败
正在加载评论插件