天天看點

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
           

本例子的調用關系圖為

Java動态代理(Java Dynamic Proxy)

使用代理的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
           

這個例子中的調用關系為:

Java動态代理(Java Dynamic Proxy)

以上這個是用代理的例子似乎也達到了我們的目的,現在,我們希望在調用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