代理模式是Java常見的設計模式之一。所謂代理模式是指客戶端並不直接調用實際的對象,而是通過調用代理,來間接的調用實際的對象。
為什么要采用這種間接的形式來調用對象呢?一般是因為客戶端不想直接訪問實際的對象,或者訪問實際的對象存在困難,因此通過一個代理對象來完成間接的訪問。
在現實生活中,這種情形非常的常見,比如請一個律師代理來打官司。
下面例子的代碼可以訪問源碼。歡迎star,歡迎fork
代理模式的UML圖
從UML圖中,可以看出代理類與真正實現的類都是繼承了抽象的主題類,這樣的好處在於代理類可以與實際的類有相同的方法,可以保證客戶端使用的透明性。
代理模式的實現
代理模式可以有兩種實現的方式,一種是靜態代理類,另一種是各大框架都喜歡的動態代理。下面我們主要講解一下這兩種代理模式
靜態代理
我們先看針對上面UML實現的例子,再看靜態代理的特點。
Subject接口的實現
public interface Subject {
void visit();
}
實現了Subject接口的兩個類:
public class RealSubject implements Subject {
private String name = "byhieg";
@Override
public void visit() {
System.out.println(name);
}
}
public class ProxySubject implements Subject{
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void visit() {
subject.visit();
}
}
具體的調用如下:
public class Client {
public static void main(String[] args) {
ProxySubject subject = new ProxySubject(new RealSubject());
subject.visit();
}
}
通過上面的代理代碼,我們可以看出代理模式的特點,代理類接受一個Subject接口的對象,任何實現該接口的對象,都可以通過代理類進行代理,增加了通用性。但是也有缺點,每一個代理類都必須實現一遍委托類(也就是realsubject)的接口,如果接口增加方法,則代理類也必須跟着修改。其次,代理類每一個接口對象對應一個委托對象,如果委托對象非常多,則靜態代理類就非常臃腫,難以勝任。
動態代理
動態代理有別於靜態代理,是根據代理的對象,動態創建代理類。這樣,就可以避免靜態代理中代理類接口過多的問題。動態代理是實現方式,是通過反射來實現的,借助Java自帶的java.lang.reflect.Proxy
,通過固定的規則生成。
其步驟如下:
- 編寫一個委托類的接口,即靜態代理的(Subject接口)
- 實現一個真正的委托類,即靜態代理的(RealSubject類)
- 創建一個動態代理類,實現
InvocationHandler
接口,並重寫該invoke
方法 - 在測試類中,生成動態代理的對象。
第一二步驟,和靜態代理一樣,不過說了。第三步,代碼如下:
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
}
第四步,創建動態代理的對象
Subject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Subject subject = (Subject) Proxy.newProxyInstance(classLoader, new Class[]{Subject.class}, proxy);
subject.visit();
創建動態代理的對象,需要借助 Proxy.newProxyInstance
。該方法的三個參數分別是:
- ClassLoader loader表示當前使用到的appClassloader。
- Class<?>[] interfaces表示目標對象實現的一組接口。
- InvocationHandler h表示當前的InvocationHandler實現實例對象。
關於動態代理的使用,我們就介紹到這里。關於動態代理的實現、借助非JDK庫實現動態代理、以及他們的優缺點放到以后再介紹。