以下内容翻译自不知名的某个文档
概述
作为client和target之间的中间人(intermediary),代理在很多场合下是很有用的。
为了进一步理解动态代理的作用,我们首先看一个不使用代理机制的实例。
不使用代理的Vehicle例子
public interface IVehicle {
public void start();
public void stop();
public String getName();
}
public class Car implements IVehicle {
String name;
public Car(String name) {
this.name = name;
}
public void start() {
System.out.println("start(): The car" + name + " started");
}
public void stop() {
System.out.println("stop(): The car" + name + " stopped");
}
public String getName() {
return this.name;
}
}
public class Client {
public static void main(String[] args) {
IVehicle v = new Car("BMW");
v.start();
v.stop();
}
}
运行结果为
start(): The carBMW started
stop(): The carBMW stopped
本例子的调用关系图为
使用代理的Vehicle例子
记住:使用代理的主要目的是为了更好地控制对target的访问,而不是增强target的功能(functionality)。
代理通过以下方式来实现对target的访问控制:
- Synchronization
- Authentication
- Remote Access
- Lazy instantiation
public class VehicleProxy implements IVehicle {
private IVehicle v;
public VehicleProxy(IVehicle v) {
this.v = v;
}
public void start() {
System.out.println("VehicleProxy.start()");
v.start();
}
public void stop() {
System.out.println("VehicleProxy.stop()");
v.stop();
}
public String getName() {
System.out.println("VehicleProxy.getName()");
return v.getName();
}
}
public class Client2 {
public static void main(String[] args) {
IVehicle car = new Car("BMW");
IVehicle v = new VehicleProxy(car);
v.start();
v.stop();
}
}
运行结果为
VehicleProxy.start()
start(): The carBMW started
VehicleProxy.stop()
stop(): The carBMW stopped
这个例子中的调用关系为:
以上这个是用代理的例子似乎也达到了我们的目的,现在,我们希望在调用Car的每一个方法的之前和之后,都能打印出一些日志信息,例如传入的参数和输出的结果。但是我们也要考虑到以下事实:
- Car类及IVehicle接口都是核心业务层面的代码,不能轻易改动;
- 目前已经有大量代码用到了Car类和IVehicle接口,不能对这些代码进行大规模的变动;
- Car类和IVehicle里也许有很多方法,我们希望花最小的代价就能为每一个方法增加调用日志
- 今后为Car类和IVehicle接口增加新的方法时,我们希望在调用这些新增方法时能够自动地增加调用日志
好了,是该看看动态代理的时候了
Java动态代理
什么是Java动态代理
- 当一个动态代理类(dynamic proxy class)在被创建之后,它将实现在运行时被指定的一系列接口
- 代理接口(proxy interface)是一个由代理类(proxy class)实现的接口
- 代理实例(proxy instance)是一个代理类(proxy class)的实例
- 每一个代理实例都有一个与之关联的invocation handler object(实现了InvocationHandler接口)
- 如果通过代理接口来调用代理实例上的一个方法,那么该方法调用将被转发(dispatch)至与该代理实例相关联的invocation handler的invoke方法
java.lang.reflect.Proxy类
Proxy类为创建动态代理类和动态代理实例提供的静态方法,同时它也是由这些静态方法创建的动态代理类的父类。
为接口Foo创建动态代理类:
InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new class[]{foo.class});
Foo foo = proxyClass.getConstructor(new class[]{InvocationHandler.class}).newInstance(new Object[]{handler});
也可以通过下列方式为接口Foo创建动态代理类:
InvocationHandler handler = new MyInvocationHandler(...);
Foo foo = Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[]{Foo.class}, handler);
动态代理实例
我们将前面的例子用动态代理的方式来做如下改造
public interface IVehicle {
public void start();
public void stop();
public String getName();
}
public class Car implements IVehicle {
String name;
public Car(String name) {
this.name = name;
}
@Override
public void start() {
System.out.println("start(): The car " + name + " started");
}
@Override
public void stop() {
System.out.println("stop(): The car " + name + " stopped");
}
@Override
public String getName() {
System.out.println("getName(): The car " + name + "'s name is retrieved");
return this.name;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
public class VehicleHandler implements InvocationHandler {
private IVehicle target;
public VehicleHandler(IVehicle target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
System.out.println("[Before Method Call] The method " + m.getName() + "() begins with " + Arrays.toString(args));
Object result = m.invoke(target, args);
System.out.println("[After Method Call] The method " + m.getName() + "() ends with " + result);
return result;
}
}
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
IVehicle car = new Car("Ford");
IVehicle proxiedCar = (IVehicle) Proxy.newProxyInstance(
car.getClass().getClassLoader(),
car.getClass().getInterfaces(),
new VehicleHandler(car));
proxiedCar.start();
System.out.println("-------------------------------------------");
String name = proxiedCar.getName();
}
}
运行结果为
[Before Method Call] The method start() begins with null
start(): The car Ford started
[After Method Call] The method start() ends with null
-------------------------------------------
[Before Method Call] The method getName() begins with null
getName(): The car Ford's name is retrieved
[After Method Call] The method getName() ends with Ford