你管這叫代理模式(Proxy Pattern)
代理模式
代理模式即給一個真實類提供一個代理類,該代理類代替真實類完成其功能,一般還可在代理類上添加一些真實類不具有的附加功能,通俗來講代理模式就是我們生活中常見的中介,代理模式又可分為靜態代理和動態代理。
靜態代理
靜態代理需要代理類和目標類實現一樣的介面,一般將被代理對象組合到代理類中,然後使用其完成對應功能,並可在代理類中添加額外功能。
租房靜態代理過程
第一步:創建服務介面
public interface Rent {
// 出租房屋
void rent();
}
第二步:實現類實現服務介面
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房東要出租房子...");
}
}
第三步:代理類實現服務介面
public class Proxy implements Rent{
// 被代理的類,用於完成被代理類功能。
private Host host;
// 用於初始化被代理類
public void setHost(Host host) {
this.host = host;
}
// 完成被代理類的功能,同時可附加其他功能。
@Override
public void rent() {
doSomething();
host.rent();
doSomething();
}
// 附加的功能
private void doSomething() {
System.out.println("doSomething...");
}
}
第四步:編寫測試類
public class Test {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy();
proxy.setHost(host);
proxy.rent();
}
}
靜態代理總結
優點
- 高擴展性:可以在不修改目標類的前提下擴展目標類的功能。
缺點
- 冗餘:一個目標類就會產生一個代理類,會產生過多的代理類。
- 開發效率低:代理類程式碼量繁重。
- 不易維護:一旦介面改變,目標類與代理類都要進行修改。
動態代理
動態代理中我們不需要手動的創建代理類,只需要編寫一個動態處理器來為我們動態創建代理對象,代理對象由 JDK 在運行時動態創建,動態代理基於反射來實現。
常用的動態代理方式
- 基於介面的動態代理:由 JDK 提供,使用 JDK 官方的 Proxy 類創建代理對象。
- 基於類的動態代理:由第三方 CGLib 提供,使用 CGLib 的 Enhancer 類創建代理對象
- javassist
- ……
基於介面的動態代理
使用基於介面的動態代理首先需要了解 JDK 中的 Proxy 類和 InvocationHandler介面,然後用其編寫動態處理器用於完成動態代理。
Proxy 類
Proxy 提供了創建動態代理類和實例的靜態方法,它也是由這些方法創建的所有動態代理類的超類。
一般使用 Proxy 的 newProxyInstance 靜態方法創建代理類,需要提供一個類載入器、被代理類的服務介面,以及一個 InvocationHandler 的對象。
- 類載入器:用於載入類和介面
- 被代理類的介面:提供被代理類服務介面資訊
- InvocationHandler 對象:用於執行被代理類的方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h);
InvocationHandler 介面
當在代理實例上調用方法時,方法調用被編碼並分派到其調用處理程式的調用方法,即在 Proxy 中調用方法時,該方法調用被分派給 InvocationHandler 對象中的 invoke 方法執行,由 invoke 方法調用執行被代理對象的方法。
Object invoke(Object proxy, Method method, Object[] args);
動態處理器工具類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的介面
private Object target;
public ProxyInvocationHandler() {
}
public ProxyInvocationHandler(Object target) {
this.target = target;
}
// 用於初始化被代理類
public void setTarget(Object target) {
this.target = target;
}
// 生成得到代理類
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 處理代理示例並返回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
基於介面的租房動態代理過程
第一步:創建服務介面
public interface Rent {
// 出租房屋
void rent();
}
第二步:實現類實現服務介面
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房東要出租房子...");
}
}
第三步:編寫動態處理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的介面
private Object target;
// 用於初始化被代理類
public void setTarget(Object target) {
this.target = target;
}
// 生成得到代理類
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 處理代理示例並返回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doSomething();
Object invoke = method.invoke(target, args);
doSomething();
return invoke;
}
// 附加的功能
private void doSomething() {
System.out.println("doSomething...");
}
}
第四步:編寫測試類
public class Test {
public static void main(String[] args) {
Host host = new Host();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(host);
Rent proxy = (Rent)proxyInvocationHandler.getProxy();
proxy.rent();
}
}
動態代理總結
優點
- 高擴展性:可以在不修改目標類的前提下擴展目標類的功能。
- 低冗餘:會產生過多的代理類。
- 低耦合:減少了對業務介面的依賴。
- 高效開發:大大減少開發任務,一個動態處理器即可完成代理。
注意事項
- 由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
- 實現代理模式需要額外的工作,有些代理模式的實現非常複雜。
- 和適配器模式的區別:適配器模式主要改變所考慮對象的介面,而代理模式不能改變所代理類的介面。
- 和裝飾器模式的區別:裝飾器模式為了增強功能,而代理模式是為了加以控制。