代理模式
我们都知道代理人是干什么的,比如有一个歌手去开演唱会,但是有很多事情:卖门票、找赞助商、宣传。这些事情会让歌手去干吗,当然不会,这时候我们创建一个助理,让所有事情都去找助理,流程就会变成这样:助理卖门票,找赞助,宣传,那么最后唱歌呢,唱歌也找助理,只不过助理本身不会唱歌,助理只能回调歌手的唱歌方法,来找歌手唱歌,这就是代理模式。
那么代理模式有什么好处呢,通过上面我们发现好像有点AOP编程的意思了,直接可以在代理类中修改真正方法前后的业务逻辑。还有一个好处就是通过修改单个代理类,完成对整个项目的修改。
如一开始整个项目网络模型用的是hutool,写了一个hutool包装了GET方法,但是又想改成okhttp了,这样的话就得改所有用到hutool的地方。

但是用代理之后,只需要将代理类的模型将原来的hutool改为okhttp即可,相当于又封装了一层。

静态代理
根据描述,我们可以轻松的写出静态代理的代码,唱歌作为一个方法放在接口中,代理和歌手都实现这个接口并重写唱歌方法,代理类内部存一个歌手类,用来回调歌手唱歌方法。
public interface Ability {
void sing();
}
public class Singer implements Ability{
@Override
public void sing() {
System.out.println("歌手唱歌");
}
}
public class SingerProxy implements Ability{
private final Ability singer;
public SingerProxy(Ability singer) {
this.singer = singer;
}
@Override
public void sing() {
//这里执行业务逻辑
System.out.println("代理卖票");
singer.sing(); // 回调真实对象的sing方法
}
}
public class Run {
public static void main(String[] args) {
Ability singer = new Singer(); // 真实对象
Ability singerProxy = new SingerProxy(singer); // 代理对象
singerProxy.sing();
}
}
结果:

这就是静态代理,思路简单,方便实现。有缺点:如果现在又来了个跳舞的,就得写一个跳舞的代理这样就会出现好多好多代理类。
解决办法:动态代理!
动态代理
动态代理是在代码运行阶段动态创建的代理类,将创建的代理类的字节码文件加载到JVM虚拟机中,相当于是在运行前没有创建代理类,而运行时是动态代理机制帮我们创建的,这个类不是显式的,所以看不到。
使用方法:
还是拿刚才的例子进行改造,动态代理就不需要我们自己再创建代理类了。
于是我们的主函数就改成了:
public class Run {
public static void main(String[] args) {
// Ability singer = new Singer();
// Ability singerProxy = new SingerProxy(singer);
// singerProxy.sing();
Ability singer = new Singer();
Ability singerProxy = (Ability) Proxy.newProxyInstance( // 代理对象
Run.class.getClassLoader(),
new Class[]{Ability.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理卖票");
return method.invoke(singer, args);
}
}
);
singerProxy.sing();
}
}
看不懂没有关系,我们接下来讲原理就懂了。
先说使用方法:主要使用到了Proxy.newProxyInstance() 方法创建出来了代理类,来看一下源码中需要传入的参数

第一个参数传一个类加载器,这里可以通过其他类的class获取加载器,也可以通过当前类的class获取,他们指向的其实都是一个加载器,这是JVM的内容。会用即可。
第二个参数要求传一个接口列表,也就是想代理哪些接口,实例化出的目标就可以用这些接口接收
第三个是传一个处理器,这个处理器里面只需要重写一个invoke方法,这个方法也就是我们真正的代理方法了。它又有三个参数,第一个不常用,指向代理对象本身,第二个是反射出来的方法。指的是某个类的方法而不是实例的具体方法,这个要搞清楚,例如:调用代理对象的sing方法后,method指代的只是sing这个方法而已,如果想要调用真实对象的方法就要调用该方法里的method.invoke(singer, args),这样就能执行singer它自己的sing方法了。第三个参数是调用者传过来的参数列表,不懂看下边这张图

简单来说就是代理对象在调用方法时回调了我们重写的invoke方法,并将参数携带过来了。
所以我们执行sing方法后先打印"代理卖票"这句话。
下面我们往下追newProxyInstance这个方法的源码:
点进去我们发现它用了一个构造器来构造,接着我们看构造器如何来的

我们看到了ProxyBuilder,接着往下追

进到build方法里,这里有一个defineProxyClass创建了一个类,通过这个类拿到构造器返回,我们看一下这个类怎么来的

下面这个方法有点长,我们直接看return部分
这里生成了一个字节数组,也是通过这个字节数组生成了类。我们就不再往下追了,下面就是一些偏底层的字节码生成,我们只需知道这个对象是由ProxyGenerator构造了一个字节数组,把字节数组转为类的class,通过class的构造器构造出类的对象。
由于ProxyGenerator.generateProxyClass() 这个方法在最新的JDK中不然调用了,我就生成不了这个类的class文件来观察了,所以我让豆包生成了一份代理对象的class文件长什么样供参考:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
// 动态生成的代理类,实现了目标接口Subject
public final class $Proxy0 extends Proxy implements Ability {
// 静态方法缓存(存储接口中的方法)
private static Method m1; // Object类的equals方法
private static Method m3; // Subject接口的Sing方法
private static Method m2; // Object类的toString方法
private static Method m0; // Object类的hashCode方法
// 构造方法,接收InvocationHandler
public $Proxy0(InvocationHandler h) throws {
super(h);
}
// 重写equals方法
public final boolean equals(Object obj) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{obj});
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
// 实现Ability接口的Sing方法
public final void Sing() throws {
try {
// 核心:通过InvocationHandler调用
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
// 重写toString方法
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
// 重写hashCode方法
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
// 静态代码块,初始化方法缓存
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("Subject").getMethod("Sing");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
}
}
简单说就是生成的这个类实现了我们刚才给他传入的接口,并且重写了方法,具体方法操作就是回调我们刚才在动态代理重写的invoke方法。 动态代理的原理我们就讲完了。下面的Retrofit也是动态代理典型的使用场景。
9163



被折叠的 条评论
为什么被折叠?



