JDK的动态代理为什么需要接口
JDK动态代理实现方式
实现步骤:
创建一个接口 ProxyInterface
1
2
3public interface ProxyInterface {
public void printSomething();
}编写他得简单实现类 ProxyImpl
1 | public class ProxyImpl implements ProxyInterface { |
编写调用处理程序InvocationHandler,实现其invoke方法,后面具体反向代理,会进入这里面按照下面invoke里面得顺序执行。三个参数,分别代表传入得代理实现类,此次调用得方法名称,有关参数。这是平时使用得动态代理得核心方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class ProxyInvocationHandler implements InvocationHandler {
private Object obj;
public void setObj(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前!");
System.out.println("proxy =" + proxy.getClass().toString());
System.out.println("method = " + method.getName());
System.out.println("args = " + args);
Object obj = method.invoke(this.obj,args);
System.out.println("代理后");
//注意 如果被代理的方法如果有返回值,这里也需要返回,否则可能会报 NullPointerException
return obj;
}
}编写调用类
1
2
3
4
5
6
7
8
9
10public class ProxyMain {
public static void main(String[] args) {
ProxyInvocationHandler demo = new ProxyInvocationHandler();
demo.setObj(new ProxyImpl());
ProxyInterface o = (ProxyInterface)Proxy.newProxyInstance(ProxyMain.class.getClassLoader(),
new Class[]{ProxyInterface.class}, demo);
o.printSomething();
}
}启动后,控制台输出
1
2
3
4
5
6代理前!
proxy =class com.sun.proxy.$Proxy0
method = printSomething
args = null
反向代理成功!
代理后
为什么JDK动态代理需要接口
主要的秘密在newProxyInstance这个方法里
1 |
|
调用getProxyClass()
1 | /** |
proxyClassCache是Proxy的静态变量,是WeakCache类,里面封装了两个类KeyFactory、ProxyClassFactory,都是BiFunction函数式接口,作转换用。KeyFactory 用与生成Cache中的key,ProxyClassFactory用于生成Cache中的value,也就是代理类。
关注ProxyClassFactory中的apply方法
1 |
|
可以看出内部调用了ProxyGenerator的generateProxyClass方法。然后再调用defineClass0方发
1 | public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { |
然后我们再来看下生成字节码的方法
代码比较长,而且东西比较多.如果不懂字节码的话,很难看懂.
这里简单的提一下class文件字节码结构
class 文件由下面十个部分组成
- 魔数(Magic Number)
- 版本号(Minor&Major Version)
- 常量池(Constant Pool)
- 类访问标记(Access Flags)
- 类索引(This Class)
- 超类索引(Super Class)
- 接口表索引(Interfaces)
- 字段表(Fields)
- 方法表(Methods)
- 属性表(Attributes)
然后我们找到给超类索引设置值的地方.
1 | private byte[] generateClassFile() { |
从上面的字节码可以看出,生成的代理类继承了java/lang/reflect/Proxy.
我们再测试下生成的文件内容
1 | package dynamic; |
看到生成的文件,反编译之后
1 | // |
在调用save方法时候,会调用 super.h.invoke(this, m3, (Object[])null);由下面的静态代码块可知,m3是我们接口实现的原生方法,而h就是我们实现的InvocationHandler
1 | public class Proxy implements java.io.Serializable { |
个人理解
因为生成的代理类集成了Proxy,我们知道java是单继承的,如果 原代理类是实现类,不能保证没有它没有继承其他类,就会出现错误,为了防止这种错误,所以就只允许代理接口。