面試會問的-java靜態代理(動態代理前篇)

內容:

  • 需求:給原有方法添加日誌打印;
  • 靜態代理實現日誌打印;
  • 靜態代理問題;

1、給原有方法添加日誌打印

假設:現有一個類 Calculator ,代表一個計算器,可以進行加減乘除操作;

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;
 }
​
 //乘法、除法...
}

在每個方法執行前後打印日誌。

直接修改:

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;
 }
​
 //乘法、除法...
}

問題:

  1. 直接修改源碼不符合開閉原則。應該對擴展開放,對修改關閉;
  2. 如果 Calculator 有很多情況下;
  3. 存在大量重複代碼;
  4. 日誌打印 硬編碼在代理類中,不利於後期維護:比如你花了一上午終於寫完了,組長告訴你這個功能取消,於是你又要打開Calculator花十分鐘刪除日誌打印的代碼

靜態代理實現日誌打印:

"靜態代理" 包含了:靜態、代理 兩個概念。

代理是一種模式,提供了對目標對象的間接訪問方式,即通過代理訪問目標對象。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求。

靜態代理的實現:編寫一個代理類,實現與目標對象相同的接口,並在內部維護一個目標對象的引用。通過構造器塞入目標對象,在代理對象中調用目標對象的同名方法,並添加前攔截,後攔截等所需的業務功能。

代理類和目標需要實現同一個接口:

  • 將 Calculator 抽取為接口;
  • 創建目標類 CalculatorImpl 實現 Calculator;
  • 創建代理類 CalculatorProxy 實現 Calculator ;

接口:

/**
 * 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);
 }
} 

靜態代理優點: (實際上只解決了第一個缺點)

  1. 直接修改源程序,不符合開閉原則。√
  2. 如果 Calculator 有很多情況下。 ×
  3. 存在重複代碼(都是在核心代碼前後打印日誌) ×
  4. 日誌打印硬編碼在代理類中,不利於後期維護:比如你花了一上午終於寫完了,組長告訴你這個功能取消,於是你又要打開Calculator花十分鐘刪除全部新增代碼!×

2、靜態代理的問題:

上述案例中,代理類要和目標類實現相同接口,即編寫了CalculatorProxy(代理對象),並通過構造器傳入CalculatorImpl(目標對象),調用目標對象同名方法的同時添加增強代碼。

這裡問題,代理對象構造器的參數是Calculator ,這意味著他只能接收Calculator 的實現類對象,即 CalculatorProxy 只能給 Calculator 做代理對象。


分享到:


相關文章: