本篇内容
- 介绍JDK动态代理的基本概念
- 一些JDK动态代理的疑问
- JDK动态代理的Demo
- JDK动态代理的原理
- 心得
主要思路
- 被代理类:实际代码类,必须实现至少一个接口 (下文中的
Man
类) - 代理类:实现代理逻辑的代理类,继承Proxy类和实现被代理类同一个接口 (由JDK动态生成)
- 接口:连接代理类和被代理类的桥梁 (下文中的Person接口)
为什么JDK动态代理必须要有接口呢?
因为生成的代理类是实现了InvocationHandler()
接口,代理的逻辑都是由这个接口的invoke()
方法实现。因此这个实现接口的对象就是由Proxy来存储,所有代理类就必须要继承Proxy,因此要桥接代理类和被代理类就只能用接口了
而为什么一定要继承Proxy
应该是想
- 制约必须是代理接口
- 如果是允许继承类,那么字段的继承会浪费很大的内存,而且会存在final无法被重写的风险
Demo
//Person接口
public interface Person {
public void name();
public void age();
}
--------------------------------------------------------------------------
//被代理类 实现了Person接口
public class Man implements Person {
public void name() {
System.out.println("我的名字是小米");
}
public void age() {
System.out.println("我今年23");
}
public void sing() {
System.out.println("我不会唱歌");
}
}
--------------------------------------------------------------------------
public class JDK {
//获取代理类,只是创建了个类来实现重写InvocationHandler.invoke()方法
static class ManProxy{
private Man man;
public ManProxy(Man man) {
this.man = man;
}
//创建代理类过程 调用Proxy.newProxyInstance()
public Person getProxy() {
return (Person) Proxy.newProxyInstance(man.getClass().getClassLoader(), man.getClass().getInterfaces(), new InvocationHandler() {
@Override
public String toString() {
return "这是InvocationHandler的toString";
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("name")) {
System.out.println("你是在问我的名字吗?");
method.invoke(man, args);
} else if (method.getName().equals("age")) {
System.out.println("你是在问我的岁数吗?");
method.invoke(man, args);
} else if (method.getName().equals("sing")) {
System.out.println("你是在问我会唱歌吗?");
method.invoke(man, args);
}else if (method.getName().equals("toString")) {
System.out.println("你执行的是toString");
System.out.println(toString());
method.invoke(man, args);
}
return null;
}
});
}
}
public static void main(String[] args) {
ManProxy manProxy = new ManProxy(new Man());
Person proxy = manProxy.getProxy();
proxy.name();
proxy.age();
System.out.println(proxy.toString());
}
}
输出:
你是在问我的名字吗?
我的名字是小米
你是在问我的岁数吗?
我今年23
你执行的是toString
这是InvocationHandler的toString
null //这个输出是main方法里面的调用代理类Proxy0中的toString()方法
核心方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException{}
JDK动态代理原理图.png过程
- 首先需要代理的类实现
InvocationHandler()
接口,并重写invoke()
方法 - 代理类生成从
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
开始- ClassLoader类加载器:保证与被代理类是同一个类加载器
- 被代理类的接口数组
- 调用
getProxyClass0()
方法- 这里首先会从缓存中查找代理类
- 如果缓存中没有,就会去创建代理类
- 创建代理类是通过
内部类ProxyClassFactory
创建的- 通过
apply()
方法创建类 - 首先是生成代理类的字节码
- 然后通过字节码来生成一个类
- 通过
- 通过代理类的构造方法来创建一个实例,而且初始化的参数是我们实现了
InvocationHandler()
接口的对象
动态生成的代理类Proxy0.class 反编译后是这样的
// 省略了部分代码
public final class $Proxy0 extends Proxy
implements person
{
// person接口的方法
private static Method m1;
private static Method m2;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
// 这个代理类也重写了person的方法,我们调用m1方法的入口就只此处
public final void m1(String paramString){
// 全部都会调用到InvocationHandler方法中的invoke中。
// invoke方法就是实现我们代理逻辑的方法
this.h.invoke(this, m1, new Object[] { paramString });
}
public final void m2(String paramString){
// 全部都会调用到InvocationHandler方法中的invoke中。
// invoke方法就是实现我们代理逻辑的方法
this.h.invoke(this, m1, new Object[] { paramString });
}
}
心得
为什么是动态
- 因为是根据被代理类的接口和类加载器生成的字节码,然后在运行时加载的这个class文件去生成对象
网友评论