代理模式:爲其他對象提供一種代理以控制某個對象的訪問。用在:在某些情況下,一個客戶不想或者不能直接訪問另一個對象,而代理對象可以在客戶端和目標對象之前起到中介的作用,代理對象還可以完成它附加的操作。
例子:就像房東、租客、中介的關係。中介(代理對象)爲房東(真實對象)出租房子,租客(客戶)通過中介(代理對象)來找房子租房子,中介完成了租房以後可以收取中介費(附加操作)。
先看看靜態代理模式,通過上面對代理模式的理解,可以瞭解到代理模式:即不直接通過new一個真實對象來調用方法,而是通過代理對象來調用一個方法,所以代理對象包含真實對象的引用。下面看一下代碼
接口:Subject包含一個方法
package com.example.designpattern.proxy;
public interface Subject {
void request();
}
RealSubject類,實現了Subject接口,爲了簡單起見,方法簡單的輸出一句話:
package com.example.designpattern.proxy;
public class RealSubject implements Subject {
//真是角色實現了
public void request() {
System.out.println("From real subject");
}
}
代理類ProxySubject,也要實現Subject接口,實現Subject裏面的方法,但是在這裏裏面是通過調用真實對象來實現的。
package com.example.designpattern.proxy;
public class ProxySubject implements Subject {
private RealSubject realSubject; //代理角色內部引用了真實角色
//代理角色實現目標動作
public void request() {
this.preRequest(); //在真實角色操作之前所附加的操作
if (realSubject == null){
realSubject = new RealSubject();
}
realSubject.request(); // 真實角色所完成的事情
this.afterRequet(); //在真實角色操作之後附加的操作
}
//代理角色之前完成的動作
private void preRequest(){
System.out.println("pre request");
}
//代理角色之後完成的動作
private void afterRequet(){
System.out.println("after request");
}
}
客戶調用者
package com.example.designpattern.proxy;
public class Client {
public static void main(String[] args) {
ProxySubject proxy = new ProxySubject();
//通過代理對象來調用方法
proxy.request();
}
}
靜態代理:
可以運行一下這些代碼哦, 可以在Client類中看到,是通過代理ProxySubject的對象proxy來調用方法的,在代理類ProxySubject中,有一個真實對象的引用,在代理對象的中request()方法調用了真實對象的方法。這樣的模式叫做代理模式。
優點是:
- 代理模式能將代理對象與真實對象被調用的目標對象分離。
- 一定程度上降低了系統的耦合度,擴展性好。
代理類中包含了對真實主題的引用,這樣做也有缺點:
- 真實對象與代理類一一對應,增加真實類也要增加代理類,這樣做會快速的增加類的數量,使得系統變得複雜。
- 設計代理以前真實主題必須事先存在,不太靈活。
採用動態代理可以解決以上問題,動態代理是相對於靜態代理來說的。
可能你也會說怎麼樣實現動態創建實例,以前我們創建實例不都是通過new 的方式來實現的嗎?
//如下
Hello hi = new Hello();
那麼動態創建實例是由Java提供的功能,就不需要我們去new 對象,他已經定義好了靜態方法Proxy.newProxyInstance(),只要傳入參數調用就可以。Java文檔裏面有哦,如圖:
Java標準庫提供了一種動態代理(DynamicProxy)的機制:可以在運行期動態創建某個interface的實例。
參數解釋:
Proxy.newProxyInstance(
ClassLoader loader, // 傳入ClassLoader
Class<?>[] interfaces, // 傳入要調用的接口的方法數組
InvocationHandler h); //傳入InvocationHandler 的實例
下面看一下動態代理例子代碼:
Subject 接口
package design.dynamicproxy;
public interface Subject {
void request(String str);
}
RealSubject類 實現 Subject 接口
package design.dynamicproxy;
public class RealSubject implements Subject {
@Override
public void request(String str) {
System.out.println("From Real Subject!" + " args:" + str );
}
}
動態代理類DynamicSubject 實現了InvocationHandler,重寫invoke()方法
package design.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 該代理類的內部屬性時Object類型,實際使用時,使用該類的構造方法傳遞一個對象
* 此外該類還實現了invoke() 方法,該方法中的method.invoke() 其實就是要調用被代理對象的要執行的方法
* 方法參數是object,表示該方法從屬於object對象,通過動態代理類,我們可以在執行真是對象的
* 方法前後可以加入一些額外的方法
*/
public class DynamicSubject implements InvocationHandler {
//引入的類型是Object的,可以隨便傳入任何一個對象
private Object object;
public DynamicSubject(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling:" + method);
//等價於realSubject的request() 方法,如果這裏不調用的話,不會調用Method 對象中的方法
method.invoke(object, args);
System.out.println("after calling:" + method);
return null;
}
}
Client類
package design.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
//下面的代碼一次性生成代理
// 動態生成了class com.sun.proxy.$Proxy0 的實例,
Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(),handler);
subject.request("eather");
System.out.println(subject.getClass()); //輸出class com.sun.proxy.$Proxy0,可以看到Proxy.newProxyInstance() 是系統自動生成的實例
}
}
在Client中可以看到,我們這裏調用方法的是 subject.request(“eather”); 這個對象subject 不是通過new DynamicSubject()生成的,而是Java內部寫好的方法在運行時動態生成對象;可能有人說
InvocationHandler handler = new DynamicSubject(realSubject);
這裏不是通過new new DynamicSubject(realSubject); 生成了一個對象嗎? 是的,但是它是InvocationHandler 類型的,主要是傳遞一個InvocationHandler類型參數給Proxy.newProxyInstance(); 即最後一個參數。通過Client類的最後一句輸出可以看到它是 class com.sun.proxy.$Proxy0 ,這是Java運行時生成的。
解決了靜態代理的難題:1. 真實對象與代理類一一對應,增加真實類也要增加代理類,這樣做會快速的增加類的數量,使得系統變得複雜。 爲什麼這麼說呢, 因爲代理類引用的類型是Object的,可以隨便傳入任何一個對象,當真實類增加時,代理類不用增加,new DynamicSubject(object); new的時候把要傳入的對象傳進去即可。
下面是Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h); 這個方法的源碼啦,可以看看,深入瞭解一下
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
生成一個代理類對象
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
使用指定的調用處理程序調用其構造函數。就是使用InvocationHandler 實例調用【要調用方法的那個類】的構造方法
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}