所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。
Proxy模式是很常见的模式,在我们生活中处处可见,例如我们买火车票不一定非要到火车站去买,可以到一些火车票的代售点去买。寄信不一定是自己去寄,可以把信委托给邮局,由邮局把信送到目的地。
以上的也可叫做静态代理,是为了区别代理模式在Java中的另一种实现——动态代理。动态代理的应用十分广泛,在struts2的拦截器中就用到了动态代理机制。
在某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种方式我们就称为代理。
这里对象 A 所属类我们称为委托类,也称为被代理类,对象 B 所属类称为代理类。
代理优点有:
根据程序运行前代理类是否已经存在,可以将代理分为静态代理和动态代理。
代理类在程序运行前已经存在的代理方式称为静态代理。
通过上面解释可以知道,由开发人员编写或是编译器生成代理类的方式都属于静态代理,如下是简单的静态代理实例:
class ClassA {
public void operateMethod1() {
};
public void operateMethod2() {
};
public void operateMethod3() {
};
}
class ClassB {
private ClassA a;
public ClassB(ClassA a) {
this.a = a;
}
public void operateMethod1() {
a.operateMethod1();
};
public void operateMethod2() {
a.operateMethod2();
}; // not export operateMethod3() }
}
上面ClassA是委托类,ClassB是代理类,ClassB中的函数都是直接调用ClassA相应函数,并且隐藏了Class的operateMethod3()函数。
静态代理中代理类和委托类也常常继承同一父类或实现同一接口。
代理类在程序运行前不存在、运行时由程序动态生成的代理方式称为动态代理。
Java 提供了动态代理的实现方式,可以在运行时刻动态生成代理类。这种代理方式的一大好处是可以方便对代理类的函数做统一或特殊处理,如记录所有函数执行时间、所有函数执行前添加验证判断、对某个特殊函数进行特殊操作,而不用像静态代理方式那样需要修改每个函数。
静态代理比较简单,本文上面已简单介绍,下面重点介绍动态代理。
interface Operate {
public void operateMethod1();
public void operateMethod2();
public void operateMethod3();
}
class OperateImpl implements Operate {
@Override
public void operateMethod1() {
System.out.println("Invoke operateMethod1");
sleep(110);
}
Operate是一个接口,定了了一些函数,我们要统计这些函数的执行时间。
OperateImpl是委托类,实现Operate接口。每个函数简单输出字符串,并等待一段时间。
动态代理要求委托类必须实现了某个接口,比如这里委托类OperateImpl实现了Operate。
class TimingInvocationHandler implements InvocationHandler {
private Object target;
public TimingInvocationHandler() {
}
public TimingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.currentTimeMillis();
Object obj = method.invoke(target, args);
System.out.println(method.getName() + " cost time is:"
+ (System.currentTimeMillis() - start));
return obj;
}
}
target属性表示委托类对象。
InvocationHandler是负责连接代理类和委托类的中间类必须实现的接口。其中只有一个
public Object invoke(Object proxy, Method method, Object[] args)
函数需要去实现,参数:
proxy表示下面2.3 通过 Proxy.newProxyInstance() 生成的代理类对象。
method表示代理对象被调用的函数。
args表示代理对象被调用的函数的参数。
调用代理对象的每个函数实际最终都是调用了InvocationHandler的invoke函数。这里我们在invoke实现中添加了开始结束计时,其中还调用了委托类对象target的相应函数,这样便完成了统计执行时间的需求。
invoke函数中我们也可以通过对method做一些判断,从而对某些函数特殊处理。
class Main { public static void main(String[] args) { // create proxy instance TimingInvocationHandler timingInvocationHandler = new TimingInvocationHandler(new OperateImpl()); Operate operate = (Operate)(Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[] {Operate.class}, timingInvocationHandler)); // call method of proxy instance operate.operateMethod1(); System.out.println(); operate.operateMethod2(); System.out.println(); operate.operateMethod3(); } }
这里我们先将委托类对象new OperateImpl()作为TimingInvocationHandler构造函数入参创建timingInvocationHandler对象;
然后通过Proxy.newProxyInstance(…)函数新建了一个代理对象,实际代理类就是在这时候动态生成的。我们调用该代理对象的函数就会调用到timingInvocationHandler的invoke函数(是不是有点类似静态代理),而invoke函数实现中调用委托类对象new OperateImpl()相应的 method(是不是有点类似静态代理)。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader表示类加载器
interfaces表示委托类的接口,生成代理类时需要实现这些接口
h是InvocationHandler实现类对象,负责连接代理类和委托类的中间类
我们可以这样理解,如上的动态代理实现实际是双层的静态代理,开发者提供了委托类 B,程序动态生成了代理类 A。开发者还需要提供一个实现了InvocationHandler的子类 C,子类 C 连接代理类 A 和委托类 B,它是代理类 A 的委托类,委托类 B 的代理类。用户直接调用代理类 A 的对象,A 将调用转发给委托类 C,委托类 C 再将调用转发给它的委托类 B。