面试会问的-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 做代理对象。


分享到:


相關文章: