代理模式:有時睹耐,一個對象不適合或者不能直接引用另一個對象辐赞,而代理對象可以在客戶端和目標對象之間起到中介的作用。代理可以靜默的解決一些業(yè)務無關的問題硝训,比如遠程响委、安全、事務窖梁、日志赘风、資源關閉等,而開發(fā)者只關心他的業(yè)務實現(xiàn)纵刘。比如一個復雜的業(yè)務操作可能需要好幾個方法來完成邀窃,但是我們可以用一個總的方法來依次調(diào)用這幾個方法即可,這個方法代理了真正的實現(xiàn)假哎。
void sum(){
int a = method1();
String b = method2();
...
}
int method1(){
// 執(zhí)行業(yè)務1
return 10;
}
String method2(int a){
// 執(zhí)行業(yè)務2
return 'AA;
}
一般形式為一個類代表另一個類的功能瞬捕。
比如老板,秘書舵抹,員工肪虎。老板比較忙,平時員工是見不到的惧蛹,就把審批等請求給秘書笋轨,然后秘書再找老板處理秆剪,處理完畢后交給員工。
如新員工要轉(zhuǎn)正爵政,需要老板批準仅讽,但是老板很忙,只負責查看評語然后做出批復钾挟。但是老板的秘書可以去找新員工的直接領導了解員工表現(xiàn)洁灵,整理直接領導對員工的評語。然后加上評語和新員工轉(zhuǎn)正申請掺出,一起給老板徽千。老板同意之后,給予新員工答復汤锨,同時秘書會把轉(zhuǎn)正申請歸檔双抽。這就可以在老板批復的動作前后增加工作,增強這個事件闲礼。
實際工作中牍汹,真正的實現(xiàn)類,只負責處理核心業(yè)務邏輯柬泽,然后代理類就可以在核心方法調(diào)用前后慎菲,做增強工作。比如記錄日志锨并,監(jiān)控性能露该,安全檢查等。
優(yōu)點:
- 職責清晰
- 代理對象作為中介第煮,保護了目標對象
- 高擴展性
組成結構:
- 抽象角色:通過接口或抽象類聲明真是角色實現(xiàn)的業(yè)務方法
- 代理角色:實現(xiàn)抽象接口解幼,在自己的實現(xiàn)方法中調(diào)用真實角色對應的方法
- 真實角色:實現(xiàn)抽象接口,真正業(yè)務邏輯
代理模式又分為靜態(tài)代理和動態(tài)代理
1)靜態(tài)代理 :就是普通的編碼包警,已經(jīng)寫好了书幕。
2)動態(tài)代理:在運行階段創(chuàng)建
在不改變原有實現(xiàn)的基礎上,實現(xiàn)如下的功能
- 日志記錄
- 權限控制
- 事務處理
靜態(tài)代理
靜態(tài)代理就是我們平時寫的代碼揽趾,比如有一個真正實現(xiàn)的類A台汇,但是客戶端不去直接調(diào)用A,而是通過B這個類篱瞎,然后B再調(diào)用A苟呐。比如spring MVC中,要插入一條數(shù)據(jù)俐筋。首先是控制器調(diào)用服務層牵素,然后服務層再去調(diào)用DAO層,數(shù)據(jù)在DAO層真正插入數(shù)據(jù)庫澄者。服務層就是一個代理笆呆,并且可以在服務層做一些特殊處理请琳,比如數(shù)據(jù)轉(zhuǎn)換等。
public interface Subject {
/**
* 簡單測試
*/
void visit();
/**
* 求和
*
* @param a
* @param b
* @return 求和后的值
*/
int sum(int a, int b);
/**
* 返回當前名稱
*
* @return
*/
String getName();
/***
* 問候
* @param name
*/
void greeting(String name);
}
public class RealSubject implements Subject {
private String name = "真正的實現(xiàn)類";
@Override
public void visit() {
System.out.println(name);
}
@Override
public int sum(int a, int b) {
return a + b;
}
@Override
public String getName() {
return "我是真正的實現(xiàn)";
}
@Override
public void greeting(String name) {
System.out.println("Hello " + name);
}
}
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void visit() {
this.subject.visit();
}
@Override
public int sum(int a, int b) {
return this.subject.sum(a, b);
}
@Override
public String getName() {
return this.subject.getName();
}
@Override
public void greeting(String name) {
this.subject.greeting(name);
}
}
public class Client {
public static void main(String[] args) {
ProxySubject proxySubject = new ProxySubject(new RealSubject());
proxySubject.visit();
System.out.println(proxySubject.getName());
proxySubject.greeting("詹姆斯");
int result = proxySubject.sum(1, 2);
System.out.println(result);
}
}
動態(tài)代理
為什么要有動態(tài)代理赠幕?
- 代理類需要有和真實實現(xiàn)類一樣有所有的方法實現(xiàn)俄精,當方法較多時,好多重復性的榕堰,機械的工作竖慧。代碼相似冗余,不易維護
- 一般一個實現(xiàn)類需要關聯(lián)一個代理類逆屡。
- 每當一個新的類實現(xiàn)接口圾旨,就需要創(chuàng)建對應的代理類
動態(tài)代理的本質(zhì)就是在運行時 動態(tài) 的生成一個新類。前提是需要有一個接口魏蔗。為什么呢砍的?因為這個新生成的類要有和目標類一樣的功能。通過接口可以保證目標類必須包含那些方法莺治。
具體實現(xiàn)手段是反射廓鞠,通過Method的調(diào)用和InvocationHandler
目前實現(xiàn)動態(tài)代理的方式有jdk Proxy和cglib,其中jdk自身實現(xiàn)有如下優(yōu)點:
- 親兒子产雹,本身就支持诫惭,不存在依賴問題和第三方版本問題
- 代碼實現(xiàn)簡單
這二種的本質(zhì)區(qū)別是翁锡,jdk需要一個接口蔓挖,然后根據(jù)這個接口來在運行時-運行時-運行時動態(tài)-動態(tài)-動態(tài)的生成一個代理類出來,而cglib是創(chuàng)建一個目標類的子類馆衔,因此目標類的關鍵方法不能聲明為final瘟判。
組成結構:
- 接口對象
- 具體實現(xiàn)類
- 實現(xiàn)InvocationHandler的通用類
public class AnimalJdkDynamicProxy implements InvocationHandler {
/**
* 01.我們不確定委托類是誰?委托類的類型 是Object
* 和委托類建立關聯(lián)關系
*/
private Object target;
/**
* 02.給我一個委托類角溃,我返回一個代理類對象
*/
public Object createProxy(Object target){
//根據(jù)傳遞的參數(shù) 進行對象的關聯(lián)
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
*
* @param proxy :代理對象
* @param method :方法名
* @param args : 參數(shù)列表
* @return
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("主人在召喚"); //系統(tǒng)級業(yè)務 開始事務
Object result= method.invoke(target,args); // 主業(yè)務
System.out.println("主人離開"); //系統(tǒng)級業(yè)務 日志處理 關閉事務
return result;
}
//創(chuàng)建測試方法
public static void main(String[] args) {
AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
Animal dog= (Animal) proxy.createProxy(new Dog());
dog.eat();
System.out.println("**************************");
dog.sleep();
}
}
動態(tài)代理一般用在什么地方拷获?主要用于各種通用框架中,平時實際項目中用到的可能不多减细。