1.代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。用戶可以在代理對象中操作原對象。
2.代理模式的一般應用:提供了對目標對象的間接訪問方式,即通過代理訪問目標對象。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求。
3.代理模式分爲靜態代理和動態代理
現在用一個小需求展示:給所有方法添加日誌打印功能 打印請求參數。
public class Calculator {
//加
public int add(int a, int b) {
int result = a + b;
return result;
}
//減
public int subtract(int a, int b) {
int result = a - b;
return result;
}
//乘法、除法...
}
4.很多時候我們都是直接在方法體內打印
public class Calculator {
//加
public int add(int a, int b) {
System.out.println("add方法開始...");
int result = a + b;
System.out.println("add方法結束...");
return result;
}
//減
public int subtract(int a, int b) {
System.out.println("subtract方法開始...");
int result = a - b;
System.out.println("subtract方法結束...");
return result;
}
//乘法、除法...
}
上面的方案是有問題的:
- 直接修改源程序,不符合開閉原則。應該對擴展開放,對修改關閉
- 如果Calculator有幾十個、上百個方法,修改量太大
- 存在重複代碼(都是在覈心代碼前後打印日誌)
- 日誌打印硬編碼在代理類中,不利於後期維護:比如你花了一上午終於寫完了,組長告訴你這個功能取消,於是你又要打開Calculator花十分鐘刪除日誌打印的代碼!
靜態代理實現日誌打印
目標接口
public interface Calculator {
//加法
public int add(int a,int b);
//減法
public int subtract(int a,int b);
}
目標接口的實現類
package com.newland.draw.jdkproxy;
public class CalculatorImpl implements Calculator{
@Override
public int add(int a, int b) {
// TODO Auto-generated method stub
return a+b;
}
@Override
public int subtract(int a, int b) {
// TODO Auto-generated method stub
return a-b;
}
}
靜態代理對象類
package com.newland.draw.jdkproxy;
public class CalculatorStaticProxy implements Calculator{
private Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
@Override
public int add(int a, int b) {
System.out.println("add方法開始...");
int result = target.add(a, b);
System.out.println("add方法結束...");
return result;
}
@Override
public int subtract(int a, int b) {
System.out.println("subtract方法開始...");
int result = target.subtract(a, b);
System.out.println("subtract方法結束...");
return result;
}
}
客戶端
package com.newland.draw.jdkproxy;
public class Client {
public static void main(String[] args) {
//把目標對象通過構造器塞入代理對象
Calculator calculator = new CalculatorStaticProxy(new CalculatorImpl());
//代理對象調用目標對象方法完成計算,並在前後打印日誌
calculator.add(1, 2);
calculator.subtract(2, 1);
}
}
輸出結果:
靜態代理的優點:可以在不修改目標對象的前提下,對目標對象進行功能的擴展和攔截。但是它也僅僅解決了上一種方案4大缺點中的第1點:
- 直接修改源程序,不符合開閉原則。應該對擴展開放,對修改關閉 √
- 如果Calculator有幾十個、上百個方法,修改量太大 ×
- 存在重複代碼(都是在覈心代碼前後打印日誌) ×
- 日誌打印硬編碼在代理類中,不利於後期維護:比如你花了一上午終於寫完了,組長告訴你這個功能取消,於是你又要打開Calculator花十分鐘刪除全部新增代碼!×
靜態代理的問題
上面案例中,代理類是我們事先編寫的,而且要和目標對象類實現相同接口。由於CalculatorImpl(目標對象)需要日誌功能,我們即編寫了CalculatorProxy(代理對象),並通過構造器傳入CalculatorImpl(目標對象),調用目標對象同名方法的同時添加增強代碼。
但是這裏有個問題!代理對象構造器的參數類型是Calculator,這意味着它只能接受Calculator的實現類對象,亦即我們寫的代理類CalculatorProxy只能給Calculator做代理,它們綁定死了!
如果現在我們系統需要全面改造,給其他類也添加日誌打印功能,就得爲其他幾百個接口都各自寫一份代理類...
自己手動寫一個類並實現接口實在太麻煩了。仔細一想,我們其實想要的並不是代理類,而是代理對象!那麼,能否讓JVM根據接口自動生成代理對象呢?
比如,有沒有一個方法,我傳入接口,它就給我自動返回代理對象呢?
答案是肯定的。這就引出了動態代理,動態代理分爲JDK代理和CGLIB動態代理,在設計模式當中就不細講了,我會放在spring中講解。
JDK動態代理地址:
CGLIB動態代理地址: