java动态代理技术解析

发布 : 2019-05-29 分类 : java 浏览 :

jdk动态代理

所谓的动态代理,就是在运行时生成一个代理类,来执行被代理类的方法。

使用

1、创建一个接口对象

1
2
3
public interface Subject {
void subject();
}

2、创建一个接口对象的实现类

1
2
3
4
5
public class RealSubject implements Subject {
@Override public void subject() {
System.out.println("real subject");
}
}

3、创建一个InvocationHandler
这个handler就是代理subject对象。至于是哪个对象,就看有哪些实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Handler implements InvocationHandler {
Subject subject;

public Handler(Subject subject) {
this.subject = subject;
}

@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("handler invoke method.");
method.invoke(subject,args);
return subject;
}
}

4、测试

1
2
3
4
5
6
7
8
9
10
public class Demo {
public static void main(String[] args) {
Subject sub = new RealSubject();

Subject subject = (Subject) Proxy.newProxyInstance(sub.getClass().getClassLoader(),sub.getClass().getInterfaces(),new Handler(sub));

System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class));
subject.subject();
}
}

原理

这里,我们可以看下生成的代理类,网络上搜索到的基本都是错误的方法,很多都是照抄的,我之前写过一篇文章,论技术人员的严谨性问题,说的就是这个现象。

1、这个$Proxy0的生成,是ProxyGenerator这个jdk包中的类操作的,我们看下他的方法:
很明显,有个saveGeneratedFiles判断,那么这个从哪里来?

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
static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();

if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Path.of(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}

return classFile;
}

2、看下这段代码:

1
2
3
4
5
/** debugging flag for saving generated class files */
private static final boolean saveGeneratedFiles =
java.security.AccessController.doPrivileged(
new GetBooleanAction(
"jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();

3、所以,正确的方式是在环境变量里加-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true。
4、执行main方法,就能看到$Proxy0.class文件了。
5、看下这个class文件:
基本明白几点:

  • 动态代理必须要实现接口,很多人问为什么需要接口,因为代理类不能多继承,只能实现接口。
  • 代理类会继承Proxy类。
  • 如果执行了具体的代理类的方法,代理类会调用invocationHandler的invoke方法。所以invocationHandler很重要。
  • 这个代理类中会有四个Method,代理类生成的时候,其中三个是hashcode,tostring,equals,剩下的就是被代理的方法。会直接初始化。
    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
    public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws {
    super(var1);
    }

    public final boolean equals(Object var1) throws {
    try {
    return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
    throw var3;
    } catch (Throwable var4) {
    throw new UndeclaredThrowableException(var4);
    }
    }

    public final void subject() throws {
    try {
    super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
    throw var2;
    } catch (Throwable var3) {
    throw new UndeclaredThrowableException(var3);
    }
    }

    public final String toString() throws {
    try {
    return (String)super.h.invoke(this, m2, (Object[])null);
    } catch (RuntimeException | Error var2) {
    throw var2;
    } catch (Throwable var3) {
    throw new UndeclaredThrowableException(var3);
    }
    }

    public final int hashCode() throws {
    try {
    return (Integer)super.h.invoke(this, m0, (Object[])null);
    } catch (RuntimeException | Error var2) {
    throw var2;
    } catch (Throwable var3) {
    throw new UndeclaredThrowableException(var3);
    }
    }

    static {
    try {
    m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
    m3 = Class.forName("proxy.Subject").getMethod("subject");
    m2 = Class.forName("java.lang.Object").getMethod("toString");
    m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
    throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
    throw new NoClassDefFoundError(var3.getMessage());
    }
    }
    }

以上就是动态代理的本质,有兴趣的同学可以继续到super中去看看。

tips:知识贵在积累。

本文作者 : braveheart
原文链接 : https://zhangjun075.github.io/passages/java动态代理技术解析/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

微信扫一扫, 向我打赏

微信扫一扫, 向我打赏

支付宝扫一扫, 向我打赏

支付宝扫一扫, 向我打赏

留下足迹