代理模式
代理模式就是給某個對象提供一個代理對象娇唯,并由代理對象控制對于原對象的訪問剔宪,即客戶不直接操控原對象歌粥,而是通過代理對象間接地操控原對象凯正。
優(yōu)勢:
給某個對象中的方法進行擴展,不用改變那個對象中的方法秸抚。
代理的實現分為:
靜態(tài)代理:代理類是在編譯時就實現好的速和。也就是說 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");
}
以上示例就是擴展了request()方法碰凶,而對于被代理對象的request方法沒有進行任何改動,代理類Proxy 在編譯時就已被實現。
動態(tài)代理
Java實現動態(tài)代理的大致步驟如下:
1.定義一個委托類和公共接口痒留。
2.自己定義一個類(調用處理器類谴麦,即實現 InvocationHandler 接口),這個類的目的是指定運行時將生成的代理類需要完成的具體任務(包括Preprocess和Postprocess)伸头,即代理類調用任何方法都會經過這個調用處理器類匾效。
3.生成代理對象(當然也會生成代理類),需要為他指定委托對象恤磷,實現的一系列接口調用處理器類的實例面哼。因此可以看出一個代理對象對應一個委托對象,對應一個調用處理器實例扫步。
名詞說明:
委托類和委托對象:委托類是一個類魔策,委托對象是委托類的實例。
代理類和代理對象:代理類是一個類河胎,代理對象是代理類的實例闯袒。
Java 實現動態(tài)代理主要涉及以下幾個類:
1.java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類游岳,即 DynamicProxyClass extends Proxy政敢。
2.java.lang.reflect.InvocationHandler: 這里稱他為"調用處理器",他是一個接口胚迫,我們動態(tài)生成的代理類需要完成的具體內容需要自己定義一個類喷户,而這個類必須實現 InvocationHandler 接口。
Proxy 類主要方法為:
//創(chuàng)建代理對象
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
這個靜態(tài)函數的第一個參數是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區(qū))访锻,第二個參數是接口(表明你這個代理類需要實現哪些接口)褪尝,第三個參數是調用處理器類實例(指定代理類中具體要干什么)。這個函數是 JDK 為了程序員方便創(chuàng)建代理對象而封裝的一個函數期犬,因此你調用newProxyInstance()時直接創(chuàng)建了代理對象(略去了創(chuàng)建代理類的代碼)河哑。其實他主要完成了以下幾個工作:
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
{
//1. 根據類加載器和接口創(chuàng)建代理類
Class clazz = Proxy.getProxyClass(loader, interfaces);
//2. 獲得代理類的帶參數的構造函數
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
//3. 創(chuàng)建代理對象停团,并制定調用處理器實例為參數傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] {handler});
}
Proxy 類還有一些靜態(tài)方法芬位,比如:
InvocationHandler getInvocationHandler(Object proxy): 獲得代理對象對應的調用處理器對象。
Class getProxyClass(ClassLoader loader, Class[] interfaces): 根據類加載器和實現的接口獲得代理類揪阶。
Proxy 類中有一個映射表遣总,映射關系為:(<ClassLoader>,(<Interfaces>,<ProxyClass>) ),可以看出一級key為類加載器轨功,根據這個一級key獲得二級映射表旭斥,二級key為接口數組,因此可以看出:一個類加載器對象和一個接口數組確定了一個代理類古涧。
public class DynamicProxyDemo01 {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); //1.創(chuàng)建委托對象
ProxyHandler handler = new ProxyHandler(realSubject); //2.創(chuàng)建調用處理器對象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler); //3.動態(tài)生成代理對象
proxySubject.request(); //4.通過代理對象調用方法
}
}
/**
* 接口
*/
interface Subject{
void request();
}
/**
* 委托類
*/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/**
* 代理類的調用處理器
*/
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====");//定義預處理的工作垂券,當然你也可以根據 method 的不同進行不同的預處理工作
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
這個函數是在代理對象調用任何一個方法時都會調用的,方法不同會導致第二個參數method不同,第一個參數是代理對象(表示哪個代理對象調用了method方法)菇爪,第二個參數是 Method 對象(表示哪個方法被調用了)算芯,第三個參數是指定調用方法的參數。
動態(tài)生成的代理類具有幾個特點:
1.繼承 Proxy 類凳宙,并實現了在Proxy.newProxyInstance()中提供的接口數組熙揍。
2.public final。
3.命名方式為 ProxyN氏涩,其中N會慢慢增加届囚,一開始是Proxy1,接下來是$Proxy2...
有一個參數為 InvocationHandler 的構造函數是尖。這個從 Proxy.newProxyInstance() 函數內部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出意系。
Java 實現動態(tài)代理的缺點:因為 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),只能針對接口創(chuàng)建代理類饺汹,不能針對類創(chuàng)建代理類蛔添。
Java 動態(tài)代理的內部實現(利用Java反射機制):
Java 是怎么保證代理對象調用的任何方法都會調用 InvocationHandler 的 invoke() 方法,
這就涉及到動態(tài)代理的內部實現兜辞。假設有一個接口 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)}); //調用了invoke方法
}
}