代理模式
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
1、静态代理
我们有一个接口Movable,实现该接口的类都是可移动的:
interface Movable{
void move();
}
public class Car implements Movable {
@Override
public void move() {
System.out.println("Car move...");
//模拟移动的时间,随机10秒以内
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Bicycle implements Movable {
@Override
public void move() {
System.out.println("Bicycle move...");
//模拟移动的时间,随机10秒以内
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
现在想要实现的功能是在不改动源码的情况下得到移动的时间,我们只能生成一个中间类,将Movale的类组合进去,这个类也实现Movale接口,但是move方法中在调用m之前之后完成相应的功能:
class MoveTime implements Movable{
Movable m;
public MoveTime(Movable m) {
this.m = m;
}
@Override
public void move() {
long s = System.currentTimeMillis();
m.move();
long e = System.currentTimeMillis();
System.out.println("move time: "+(e-s));
}
}
main函数,当要得到Car的move时间时,new Car,当想得到Bicycle的move时间时,new Bicycle:
public class Main {
public static void main(String[] args) {
MoveTime c = new MoveTime(new Car());
c.move();
}
}
MoveTime是代理类,Car、Bicycle都是被代理的对象。
静态代理实现了在不改变原有代码的情况下,完成对已有功能的增强,所谓增强,就是在完成原有功能的基础上添加新的功能。
静态代理的缺点是一个代理类只能实现一类接口的代理,当我们要对除Movale之外的接口类代理时,需要自己再手写一个代理类,一类接口对应一个代理类,这样做十分麻烦,于是就有了动态代理。
2、动态代理
动态代理是静态代理的一种进化,静态代理的缺点已经很明显,动态代理解决了这个问题。
首先需要明确的是,动态代理解决的是代理类的问题,那它是怎么实现代理类的生成呢?先看代码:
//接口与Car类完全不动
interface Movable{
void move();
}
public class Car implements Movable {
@Override
public void move() {
System.out.println("Car move...");
//模拟移动的时间,随机10秒以内
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
主函数调用:
public static void main(String[] args) {
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Car car = new Car();
Movable m = (Movable) Proxy.newProxyInstance
(
Car.class.getClassLoader(),
Car.class.getInterfaces(),
new MoveTimeProxy(car)
);
m.move();
}
Movable m = (Movable) Proxy.newProxyInstance(Car.class.getClassLoader(),Car.class.getInterfaces(),new MoveTimeProxy(car));
这句话是通过newProxyInstance方法生成一个代理类,其中的参数:
- 指定用哪个类加载器加载(
ClassLoader loader
) - 指定该代理类要实现哪个接口(
Class<?>[] interfaces
) - 指定调用处理器。(
InvocationHandler h
)
其中最主要的是InvocationHandler
,这个类主要实现处理过程,正如静态代理中的计算时间等功能,都通过这个类来实现:
//声明一个实现InvocationHandler接口的类,并重写invoke方法。
public class MoveTimeProxy implements InvocationHandler {
Object obj;
public MoveTimeProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long s = System.currentTimeMillis();
Object result = method.invoke(obj,args);
long e = System.currentTimeMillis();
System.out.println("move time: "+(e-s));
return result;
}
}
主函数调用结果:
//Car move...
//move time: 5000
不过我们反过来再看主函数,也没有调用MoveTimeProxy中的invoke方法啊,在哪里调用的呢?
执行过程分析:
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
这句话是将动态代理生成的类进行保存。当执行后会发现生成了一个$Proxy0.class的文件,这个文件就是动态代理中间生成的文件,我们通过IDEA的反编译可以看到这个类的全貌:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package proxy.dynamicsproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements Movable {
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 move() 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.dynamicsproxy.Movable").getMethod("move");
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());
}
}
}
这个类继承自Proxy,实现了Movable接口。
而这个类的构造函数:
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
其中的参数是InvocationHandler,而我们自己写的处理类也是实现了InvocationHandler这个接口,所以这里的var1就是我们写的MoveTime类。我们再点进去super看一眼:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
此时将我们的MoveTime类对象已经传给了this.h。记住这里,下面会讲到。
再回来看.class文件,其中有move方法:
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
这个方法中调用的是super.h.invoke,惊不惊喜,意不意外,super.h就是我们传入的MoveTime类对象,调用它的invoke,就是我们自己写的那个invoke方法
所以大致流程是:当我们调用动态代理生成的类的move方法时,实则调用的是$Proxy0这个对象的的move方法,而这个方法中负责调用我们自己写的处理函数,也就是我们自己写的invoke方法。
而这个类是怎么生成的呢?
采用java 的asm框架,直接对java字节码进行操作,从而生成一个新类。