代理模式
代理模式就是給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。
優(yōu)勢:
給某個對象中的方法進(jìn)行擴(kuò)展,不用改變那個對象中的方法。
代理的實現(xiàn)分為:
靜態(tài)代理:代理類是在編譯時就實現(xiàn)好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
動態(tài)代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的 class 文件,而是在運行時動態(tài)生成的類字節(jié)碼,并加載到JVM中。
靜態(tài)代理示例:
public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
interface Subject{
void request();
}
class RealSubject implements Subject{
public void request(){
System.out.println("request");
}
}
class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
以上示例就是擴(kuò)展了request()方法,而對于被代理對象的request方法沒有進(jìn)行任何改動,代理類Proxy 在編譯時就已被實現(xiàn)。
動態(tài)代理
Java實現(xiàn)動態(tài)代理的大致步驟如下:
1.定義一個委托類和公共接口。
2.自己定義一個類(調(diào)用處理器類,即實現(xiàn) InvocationHandler 接口),這個類的目的是指定運行時將生成的代理類需要完成的具體任務(wù)(包括Preprocess和Postprocess),即代理類調(diào)用任何方法都會經(jīng)過這個調(diào)用處理器類。
3.生成代理對象(當(dāng)然也會生成代理類),需要為他指定委托對象,實現(xiàn)的一系列接口調(diào)用處理器類的實例。因此可以看出一個代理對象對應(yīng)一個委托對象,對應(yīng)一個調(diào)用處理器實例。
名詞說明:
委托類和委托對象:委托類是一個類,委托對象是委托類的實例。
代理類和代理對象:代理類是一個類,代理對象是代理類的實例。
Java 實現(xiàn)動態(tài)代理主要涉及以下幾個類:
1.java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類,即 DynamicProxyClass extends Proxy。
2.java.lang.reflect.InvocationHandler: 這里稱他為"調(diào)用處理器",他是一個接口,我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類,而這個類必須實現(xiàn) InvocationHandler 接口。
Proxy 類主要方法為:
//創(chuàng)建代理對象
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
這個靜態(tài)函數(shù)的第一個參數(shù)是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區(qū)),第二個參數(shù)是接口(表明你這個代理類需要實現(xiàn)哪些接口),第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具體要干什么)。這個函數(shù)是 JDK 為了程序員方便創(chuàng)建代理對象而封裝的一個函數(shù),因此你調(diào)用newProxyInstance()時直接創(chuàng)建了代理對象(略去了創(chuàng)建代理類的代碼)。其實他主要完成了以下幾個工作:
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
{
//1. 根據(jù)類加載器和接口創(chuàng)建代理類
Class clazz = Proxy.getProxyClass(loader, interfaces);
//2. 獲得代理類的帶參數(shù)的構(gòu)造函數(shù)
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
//3. 創(chuàng)建代理對象,并制定調(diào)用處理器實例為參數(shù)傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] {handler});
}
Proxy 類還有一些靜態(tài)方法,比如:
InvocationHandler getInvocationHandler(Object proxy): 獲得代理對象對應(yīng)的調(diào)用處理器對象。
Class getProxyClass(ClassLoader loader, Class[] interfaces): 根據(jù)類加載器和實現(xiàn)的接口獲得代理類。
Proxy 類中有一個映射表,映射關(guān)系為:(<ClassLoader>,(<Interfaces>,<ProxyClass>) ),可以看出一級key為類加載器,根據(jù)這個一級key獲得二級映射表,二級key為接口數(shù)組,因此可以看出:一個類加載器對象和一個接口數(shù)組確定了一個代理類。
public class DynamicProxyDemo01 {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); //1.創(chuàng)建委托對象
ProxyHandler handler = new ProxyHandler(realSubject); //2.創(chuàng)建調(diào)用處理器對象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler); //3.動態(tài)生成代理對象
proxySubject.request(); //4.通過代理對象調(diào)用方法
}
}
/**
* 接口
*/
interface Subject{
void request();
}
/**
* 委托類
*/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/**
* 代理類的調(diào)用處理器
*/
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("====before====");//定義預(yù)處理的工作,當(dāng)然你也可以根據(jù) method 的不同進(jìn)行不同的預(yù)處理工作
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
這個函數(shù)是在代理對象調(diào)用任何一個方法時都會調(diào)用的,方法不同會導(dǎo)致第二個參數(shù)method不同,第一個參數(shù)是代理對象(表示哪個代理對象調(diào)用了method方法),第二個參數(shù)是 Method 對象(表示哪個方法被調(diào)用了),第三個參數(shù)是指定調(diào)用方法的參數(shù)。
動態(tài)生成的代理類具有幾個特點:
1.繼承 Proxy 類,并實現(xiàn)了在Proxy.newProxyInstance()中提供的接口數(shù)組。
2.public final。
3.命名方式為 ProxyN,其中N會慢慢增加,一開始是Proxy1,接下來是$Proxy2...
有一個參數(shù)為 InvocationHandler 的構(gòu)造函數(shù)。這個從 Proxy.newProxyInstance() 函數(shù)內(nèi)部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出。
Java 實現(xiàn)動態(tài)代理的缺點:因為 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),只能針對接口創(chuàng)建代理類,不能針對類創(chuàng)建代理類。
Java 動態(tài)代理的內(nèi)部實現(xiàn)(利用Java反射機(jī)制):
Java 是怎么保證代理對象調(diào)用的任何方法都會調(diào)用 InvocationHandler 的 invoke() 方法,
這就涉及到動態(tài)代理的內(nèi)部實現(xiàn)。假設(shè)有一個接口 Subject,且里面有 int request(int i) 方法,則生成的代理類大致如下:
public final class $Proxy1 extends Proxy implements Subject{
private InvocationHandler h;
private $Proxy1(){}
public $Proxy1(InvocationHandler h){
this.h = h;
}
public int request(int i){
Method method = Subject.class.getMethod("request", new Class[]{int.class}); //創(chuàng)建method對象
return (Integer)h.invoke(this, method, new Object[]{new Integer(i)}); //調(diào)用了invoke方法
}
}