目錄
現實例子
什麼是代理模式?舉個例子來說明:假如說我現在想買一輛二手車,雖然我可以自己去找車源,做質量檢測等一系列的車輛過戶流程,但是這確實太浪費我得時間和精力了。我只是想買一輛車而已爲什麼我還要額外做這麼多事呢?於是我就通過中介公司來買車車,他們來給我找車源,幫我辦理車輛過戶流程,我只是負責選擇自己喜歡的車,然後付錢就可以了。
再比如打官司, 爲什麼要找個律師? 因爲你不想參與中間過程的是是非非, 只要完成自己的答辯就成, 其他的事前調查、事後追查等都由律師來搞定, 這就是爲了減輕你的負擔。
概念
爲其他對象提供一種代理,以控制對這個對象的訪問。代理對象在客戶端和目標對象之間起到中介的作用。
使用場景
1、限制目標對象。
2、保護目標對象。
3、增強目標對象。
作用和意義
1、中介隔離作用
在某些情況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象可以在客戶類和委託對象之間起到中介的作用,其特徵是代理類和委託類實現相同的接口。
2、開閉原則,增加功能
代理類除了是客戶類和委託類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類,符合代碼設計的開閉原則。
代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後對返回結果的處理等。代理類本身並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能還是由委託類來實現,但是可以在業務功能執行的前後加入一些公共的服務。例如我們想給項目加入緩存、日誌這些功能,我們就可以使用代理類來完成,而沒必要打開已經封裝好的委託類。
優缺點
優點
1、代理模式能將代理對象與真實對象被調用的目標對象分離。
2、一定程度上降低了系統的耦合度,擴展性好。
3、保護目標對象。
4、增強目標對象。
缺點
1、代理模式會造成系統設計中類的數目的增加。
2、在客戶端和目標對象增加一個代理對象,會造成請求處理速度變慢。
3、增加了系統的複雜度。
UML結構圖
角色構成
抽象主題(Subject)類
通過接口或抽象類聲明真實主題和代理對象實現的業務方法。
真實主題(Real Subject)類
實現了抽象主題中的具體業務,是代理對象所代表的真實對象,是最終要引用的對象。
代理(Proxy)類
提供了與真實主題相同的接口,其內部含有對真實主題的引用,它可以訪問、控制或擴展真實主題的功能。
代理模式的種類
按照代理創建的時期來進行分類的話:可以分爲兩種:靜態代理、動態代理。
靜態代理
定義
代理類在編譯期就生成。
優點
可以做到在符合開閉原則的情況下對目標對象進行功能擴展。
缺點
我們得爲每一個服務都得創建代理類,工作量太大,不易管理。同時接口一旦發生改變,代理類也得相應修改。
動態代理
定義
代理類在程序運行時,運用反射機制動態創建而成。
小結
雖然相對於靜態代理,動態代理大大減少了我們的開發任務,同時減少了對業務接口的依賴,降低了耦合度。但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏。
靜態代理和動態代理區別和聯繫
共同點
兩種代理模式實現都在不改動基礎對象的前提下,對基礎對象進行訪問控制和擴展,符合開閉原則。
不同點
靜態代理在程序規模稍大時,重複性和脆弱性的缺點凸顯;動態代理(搭配泛型參數)實現了一個代理同時處理N多個基礎接口,本質上是代理類和基礎接口的解耦,一定程度上規避了靜態代理的缺點。
從原理上講,靜態代理的代理類Class文件在編譯期生成,而動態代理的代理類Class文件在運行時生成,代理類在編譯階段並不存在,代理關係直到運行時才確定。
代碼實現
靜態代理
ILogger.java
package pattern.proxy.staticproxy;
/**
* <一句話功能簡述>代理模式:靜態代理模式:抽象主題角色<br/>
* <p>
* <功能詳細描述>創建一個代理類和真實類的共同接口ILogger
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public interface ILogger {
/**
* 功能描述: 打印日誌<br>
*
* @author 劉斌
* @date 2020/3/7 20:00
* @see [相關類/方法](可選)
* @since [產品/模塊版本](可選)
*/
void printMessage();
}
RealLog.java
package pattern.proxy.staticproxy;
/**
* <一句話功能簡述>代理模式:靜態代理模式:真實主題角色<br/>
* <p>
* <功能詳細描述>被代理類,現在想要在不改動被代理類代碼的前提下,實現對printMessage()方法前後進行操作
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class RealLog implements ILogger {
public RealLog() {
}
@Override
public void printMessage() {
System.out.println("打印日誌");
}
}
LogProxy.java
package pattern.proxy.staticproxy;
/**
* <一句話功能簡述>代理模式:靜態代理模式:代理類角色<br/>
* <p>
* <功能詳細描述>被代理類,現在想要在不改動被代理類代碼的前提下,實現對printMessage()方法前後進行操作
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class LogProxy implements ILogger {
// 定義一個被代理類的對象
private RealLog log;
public LogProxy() {
}
// 這個構造器傳入的是一個接口的對象
public LogProxy(RealLog realLog) {
this.log = realLog;
}
// 代理代理打印日誌信息
@Override
public void printMessage() {
preGive();
if (null == log) {
log = new RealLog();
}
log.printMessage();
postGive();
}
public void preGive() {
System.out.println("Before:在printMessage之前進行一些操作");
}
public void postGive() {
System.out.println("After:在printMessage之後進行一些操作");
}
}
StaticProxyClient.java
package pattern.proxy.staticproxy;
/**
* <一句話功能簡述>代理模式:靜態代理模式:客戶端調用<br/>
* <p>
* <功能詳細描述>現在想要在不改動被代理類代碼的前提下,實現對printMessage()方法前後進行操作
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class StaticProxyClient {
public static void main(String[] args) {
ILogger log = new LogProxy();
log.printMessage();
}
}
靜態代理控制檯輸出
動態代理
LogUtils.java(只是工具類)
package utils;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* <一句話功能簡述>日誌打印工具類<br/>
* <p>
* <功能詳細描述>
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class LogUtils {
public static void startLog(Method method, Object... objects) {
System.out.println("【" + method.getName() + "】執行開始了,他的參數爲【" + Arrays.asList(objects) + "】");
}
public static void endLog(Method method, Object... objects) {
System.out.println("【" + method.getName() + "】執行結束了,他的結果爲【" + Arrays.asList(objects) + "】");
}
public static void exceptionLog(Method method, Exception e) {
System.out.println("【" + method.getName() + "】執行出錯了,錯誤信息爲【" + e.getCause() + "】");
}
public static void finallyLog(Method method) {
System.out.println("【" + method.getName() + "】最終執行結束了");
}
}
ICalculator.java
package pattern.proxy.dynamicproxy;
/**
* <一句話功能簡述>代理模式:動態代理模式:抽象主題角色<br/>
* <p>
* <功能詳細描述>創定義一個計算機接口
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public interface ICalculator {
public int add(int num1, int mu2);
public int sub(int num1, int mu2);
public int mul(int num1, int mu2);
public int div(int num1, int mu2);
}
MyCalculatorImpl.java
package pattern.proxy.dynamicproxy;
/**
* <一句話功能簡述>代理模式:動態代理模式:真實主題角色<br/>
* <p>
* <功能詳細描述>被代理類
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class MyCalculatorImpl implements ICalculator {
@Override
public int add(int i, int j) {
//手動爲方法添加日誌
//LogUtils.startLog(i,j,"加法");
int result;
result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result;
result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result;
result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result;
result = i / j;
return result;
}
}
CalculatorProxy.java
package pattern.proxy.dynamicproxy;
import utils.LogUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* <一句話功能簡述>代理模式:動態代理模式:代理類角色<br/>
* <p>
* <功能詳細描述>被代理類,現在想要在不改動被代理類代碼的前提下,實現功能的擴展
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class CalculatorProxy {
public static ICalculator getPoxy(ICalculator myCalcultor) {
//爲執行Proxy.newProxyInstance(loader, interfaces, h),三個參數賦值
ClassLoader classLoader = myCalcultor.getClass().getClassLoader();
Class<?>[] interfaces = myCalcultor.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//對原方法添加日誌
Object result = null;
try {
LogUtils.startLog(method, args);
result = method.invoke(myCalcultor, args);
LogUtils.endLog(method, result);
} catch (Exception e) {
LogUtils.exceptionLog(method, e);
} finally {
LogUtils.finallyLog(method);
}
return result;
}
};
//生成代理對象
ICalculator proxy = (ICalculator) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return proxy;
}
}
DynamicProxyClient.java
package pattern.proxy.dynamicproxy;
/**
* <一句話功能簡述>代理模式:動態代理模式:客戶端調用<br/>
* <p>
* <功能詳細描述>
*
* @author 劉斌
* @date 2020/3/7
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class DynamicProxyClient {
public static void main(String[] args) {
originCalc();
}
public static void originCalc() {
ICalculator myCalculator = new MyCalculatorImpl();
//int result=myCalculator.add(2, 4);
//採用動態代理爲方法添加日誌
ICalculator proxy = CalculatorProxy.getPoxy(myCalculator);
proxy.add(2, 4);
proxy.div(2, 1);
}
}
動態代理控制檯輸出