以下内容翻譯自不知名的某個文檔
概述
作為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