標籤:
代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委託類預先處理訊息,過濾訊息並轉寄訊息,以及進行訊息被委託類執行後的後續處理。
相關類及介面
java.lang.reflect.Proxy:這是 Java 動態代理機制的主類,它提供了一組靜態方法來為一組介面動態地組建代理程式類及其對象。
// 方法 1: 該方法用於擷取指定代理對象所關聯的調用處理器
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:該方法用於擷取關聯於指定類裝載器和一組介面的動態代理類的類對象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:該方法用於判斷指定類對象是否是一個動態代理類
static boolean isProxyClass(Class cl)
// 方法 4:該方法用於為指定類裝載器、一組介面及調用處理器產生動態代理類執行個體
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)
java.lang.reflect.InvocationHandler:這是調用處理器介面,它自訂了一個 invoke 方法,用於集中處理在動態代理類對象上的方法調用,通常在該方法中實現對委託類的代理訪問。每次產生動態代理類對象時都需要指定一個實現了該介面的調用處理器對象。
// 該方法負責集中處理動態代理類上的所有方法調用。第一個參數既是代理類執行個體,第二個參數是被調用的方法對象
// 第三個方法是調用參數。調用處理器根據這三個參數進行預先處理或指派到委託類執行個體上發射執行
Object invoke(Object proxy, Method method, Object[] args)java.lang.ClassLoader:這是類裝載器類,負責將類的位元組碼裝載到 JAVA 虛擬機器(JVM)中並為其定義類對象,然後該類才能被使用。Proxy 靜態方法產生動態代理類同樣需要通過類裝載器來進行裝載才能使用,java開發培訓它與普通類的唯一區別就是其位元組碼是由 JVM 在運行時動態產生的而非預存在於任何一個 .class 檔案中。
步驟
動態代理具體有如下四步驟:
1. 通過實現 InvocationHandler 介面建立自己的調用處理器;
2. 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來建立動態代理類;
3. 通過反射機制獲得動態代理類的建構函式,其唯一參數類型是調用處理器介面類型;
4. 通過建構函式建立動態代理類執行個體,構造時調用處理器對象作為參數被傳入。
樣本
package twenz.test;
public interface UserDao {
public void save(String name);
}
package twenz.test;
public class UserDaoImp implements UserDao {
@Override
public void save(String name) {
// TODO Auto-generated method stub
System.out.println(this.getClass().getName());
System.out.println(name);
}
}
package twenz.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest2 implements InvocationHandler {
private UserDao target;
public ProxyTest2(UserDao target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
//System.out.println("The object is:"+proxy.getClass().getName());
System.out.println("The method is:"+method.getName());
method.invoke(target, args);
return null;
}
public static void test(UserDao target) {
ProxyTest2 proxy = new ProxyTest2(target);
UserDao user = (UserDao)Proxy.newProxyInstance(ProxyTest2.class.getClassLoader(),
target.getClass().getInterfaces(), proxy);
user.save("=");
}
}
通過調用”ProxyTest2.test(new UserDaoImp());“則可以觸發代理,會執行ProxyTest2的invoke函數並進而觸發UserDaoImp的save函數
只對介面的代理
這個才是本文關注的重點,那就是如果我沒有實現介面,只想調用UserDao的save函數該如何?下面這個代碼是一個樣本:
package twenz.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest implements InvocationHandler {
private Class<UserDao> target;
public ProxyTest(Class<UserDao> target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
//System.out.println("The object is:"+proxy.getClass().getName());
System.out.println("The method is:"+method.getName());
return null;
}
public static void test(Class<UserDao> target) {
ProxyTest proxy = new ProxyTest(target);
UserDao user = (UserDao)Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),
new Class<?>[]{target}, proxy);
user.save("");
}
public static void main(String args[])
{
ProxyTest.test(UserDao.class);
}
}
這裡我們沒有實現介面UserDao,但是我們卻調用了介面的函數save。不過這裡invoke裡面其實並沒有invoke代理的函數save,只是擷取了save的方法名。
Java開發純介面的動態代理