Java動態代理(Java Dynamic Proxy)

以下內容翻譯自不知名的某個文檔


概述

作爲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














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章