【Java基礎】代理 - Proxy

王曉斌發表於2014-03-17

利用代理可以在執行時建立一個實現了一組給定介面的新類。這種功能只有在編譯時無法確定需要實現哪個介面時才有必要使用。

代理類是在程式執行過程中建立的。然而,一旦被建立,就變成了常規類,與虛擬機器中的任何其他類沒有什麼區別。

對於特定的類載入器和預設的一組介面來說,只能有一個代理類。也就是說,如果使用同一個類載入器和介面陣列呼叫兩次newProxyInstance方法的話,只能夠得到同一個類的兩個物件。


代理實際上就是java.lang.reflect.Proxy動態地根據所指定的介面生成的class byte。該類會繼承Proxy類,並實現所有指定的介面(interfaces引數),最後使用參入的class loader將class byte載入如系統,生成這樣的一個類的物件。

在最後的一個示例中,可以看到生成的代理類的繼承體系及實現的介面。

代理類 - Proxy

public class Proxy extends Object implements Serializable (java.lang.reflect)

要想建立一個代理物件,需要使用Proxy類的newProxyInstance方法,這個方法有三個引數:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
  • 一個類載入器:作為Java安全模型的一部分,對於系統類和從因特網上下載來的類,可以使用不同的類載入器。使用null表示使用預設的類載入器(系統類的載入器)。
  • 一個Class物件陣列,每個元素都是需要實現的介面。
  • 一個呼叫處理器。

呼叫處理器 - InvocationHandler

public interface InvocationHandler (java.lang.reflect)

Modifier and Type Method and Description
Object invoke(Object proxy, Method method, Object[] args)
Processes a method invocation on a proxy instance and returns the result.
該方法定義了代理物件呼叫方法時希望執行的動作。

當呼叫代理類時,代理類只會呼叫InvocationHandler的invoke方法,所以真正實現的方法必須在invoke方法中去呼叫。


示例

系統介面代理

Integer類實現了Comparable介面。

import java.util.*;
import java.lang.reflect.*;

public class ProxyTest {
    public static void main(String[] args){
    	Object[] elements = new Object[100];
    	
    	// fill elements with proxies for the integer
    	for (int i = 0; i < elements.length; i++){
    	 Integer value = i + 1;
    	 Class[] interfaces = value.getClass().getInterfaces();
    	 InvocationHandler handler = new TraceHandler(value);
    	 Object proxy = Proxy.newProxyInstance(null, interfaces, handler);
    	 elements[i] = proxy;
    	}
    	
    	// construct a random integer
    	Integer key = new Random().nextInt(elements.length) + 1;
    	
    	// search for the key
    	int result = Arrays.binarySearch(elements, key);
    	
    	if (result >= 0){
    	 System.out.println(elements[result]);
    	}
    }
}

class TraceHandler implements InvocationHandler{
private Object target;

public TraceHandler(Object t){
target = t;
}

public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
// print the invoked method info
System.out.print("Proxy invoke: ");
System.out.print(target);
System.out.print("." + m.getName() + "(");
if (args != null){
for (int i = 0; i < args.length; i++){
System.out.print(args[i]);
if (i < args.length - 1){
System.out.print(", ");
}
}
}
System.out.println(")");

// invoke actual method
return m.invoke(target, args);
}
}


自定義介面代理

import java.util.*;
import java.lang.reflect.*;

public class ProxyTest2 {
    public static void main(String[] args){
    	    TestClass testClass = new TestClass();
    	 Class[] interfaces = testClass.getClass().getInterfaces();
    	 InvocationHandler handler = new TraceHandler2(testClass);
    	 Object proxy = Proxy.newProxyInstance(testClass.getClass().getClassLoader(), interfaces, handler);
    	 ((TestInterface)proxy).test();
    	 //((TestClass)proxy).test();   error; Proxy cannot be cast to TestClass
    	 //((TestClass)proxy).test2();  error; Proxy cannot be cast to TestClass
    }
}

interface TestInterface{
void test();
}

class TestClass implements TestInterface{
    public void test(){
    	System.out.println("TestClass.test");
    }
  
    public void test2(){
    	System.out.println("TestClass.test2");
    }
    
    public String toString(){
    	return "TestClass Instance";
    }
}

class TraceHandler2 implements InvocationHandler{
private Object target;

public TraceHandler2(Object t){
target = t;
}

public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
// print the invoked method info
System.out.print("Proxy invoke: ");
System.out.print(target);
System.out.print("." + m.getName() + "(");
if (args != null){
for (int i = 0; i < args.length; i++){
System.out.print(args[i]);
if (i < args.length - 1){
System.out.print(", ");
}
}
}
System.out.println(")");

// invoke actual method
return m.invoke(target, args);
}
}

列印代理類

我們使用反射中的列印類資訊的方法,檢視上面示例中生成的代理類的定義:

public class ReflectionTest{
	public static void main(String[] args){
            TestClass testClass = new TestClass();
            Class[] interfaces = testClass.getClass().getInterfaces();
            InvocationHandler handler = new TraceHandler2(testClass);
            Object proxy = Proxy.newProxyInstance(testClass.getClass().getClassLoader(), interfaces, handler);

            ClassAnalyzer.printClass(proxy.getClass());
	}
}

輸出:



相關文章