假設如今咱們有一個類Calculator,表明一個計算器,它能夠進行加減乘除操做html
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; } //乘法、除法... }
現有一個需求:在每一個方法執行先後打印日誌。你有什麼好的方案?java
直接修改ide
不少人最直觀的想法是直接修改Calculator類:this
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; } //乘法、除法... }
上面的方案是有問題的:編碼
因此,此種方案PASS!spa
「靜態代理」四個字包含了兩個概念:靜態、代理。咱們先來了解什麼叫「代理」,至於何爲「靜態」,須要和「動態」對比着講。3d
代理是一種模式,提供了對目標對象的間接訪問方式,即經過代理訪問目標對象。如此便於在目標實現的基礎上增長額外的功能操做,前攔截,後攔截等,以知足自身的業務需求。
引用博客: Java靜態代理和動態代理 - 紀煜楷 - 博客園
引用自:Java靜態代理和動態代理 - 紀煜楷 - 博客園代理
經常使用的代理方式能夠粗分爲:靜態代理和動態代理。日誌
靜態代理的實現比較簡單:編寫一個代理類,實現與目標對象相同的接口,並在內部維護一個目標對象的引用。經過構造器塞入目標對象,在代理對象中調用目標對象的同名方法,並添加前攔截,後攔截等所需的業務功能。code
按上面的描述,代理類和目標類須要實現同一個接口,因此我打算這樣作:
接口
/** * Calculator接口 */ public interface Calculator { int add(int a, int b); int subtract(int a, int b); }
目標對象實現類
/** * 目標對象實現類,實現Calculator接口 */ public class CalculatorImpl implements 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; } //乘法、除法... }
代理對象實現類
/** * 代理對象實現類,實現Calculator接口 */ public class CalculatorProxy implements Calculator { //代理對象內部維護一個目標對象引用 private Calculator target; //構造方法,傳入目標對象 public CalculatorProxy(Calculator target) { this.target = target; } //調用目標對象的add,並在先後打印日誌 @Override public int add(int a, int b) { System.out.println("add方法開始..."); int result = target.add(a, b); System.out.println("add方法結束..."); return result; } //調用目標對象的subtract,並在先後打印日誌 @Override public int subtract(int a, int b) { System.out.println("subtract方法開始..."); int result = target.subtract(a, b); System.out.println("subtract方法結束..."); return result; } //乘法、除法... }
使用代理對象完成加減乘除,而且打印日誌
public class Test { public static void main(String[] args) { //把目標對象經過構造器塞入代理對象 Calculator calculator = new CalculatorProxy(new CalculatorImpl()); //代理對象調用目標對象方法完成計算,並在先後打印日誌 calculator.add(1, 2); calculator.subtract(2, 1); } }
靜態代理示意圖
靜態代理的優勢:能夠在不修改目標對象的前提下,對目標對象進行功能的擴展和攔截。可是它也僅僅解決了上一種方案4大缺點中的第1點:
上面案例中,代理類是咱們事先編寫的,並且要和目標對象類實現相同接口。因爲CalculatorImpl(目標對象)須要日誌功能,咱們即編寫了CalculatorProxy(代理對象),並經過構造器傳入CalculatorImpl(目標對象),調用目標對象同名方法的同時添加加強代碼。
可是這裏有個問題!代理對象構造器的參數類型是Calculator,這意味着它只能接受Calculator的實現類對象,亦即咱們寫的代理類CalculatorProxy只能給Calculator作代理,它們綁定死了!
若是如今咱們系統須要全面改造,給其餘類也添加日誌打印功能,就得爲其餘幾百個接口都各自寫一份代理類...
本身手動寫一個類並實現接口實在太麻煩了。仔細一想,咱們其實想要的並非代理類,而是代理對象!那麼,可否讓JVM根據接口自動生成代理對象呢?
好比,有沒有一個方法,我傳入接口,它就給我自動返回代理對象呢?
答案是確定的。