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