—-慢慢来比较快,虚心学技术—-

概念

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

推演

需求**:实现计算器简单的运算**

  1. /**
  2. * 计算器
  3. */
  4. class Calculator{
  5. /**
  6. * 加
  7. */
  8. public int add(int a,int b){
  9. return a+b;
  10. }
  11. /**
  12. * 减
  13. */
  14. public int sub(int a,int b) {
  15. return a-b;
  16. }
  17. /**
  18. * 乘
  19. */
  20. public int mult(int a,int b){
  21. return a*b;
  22. }
  23. /**
  24. * 除
  25. */
  26. public int div(int a,int b){
  27. return a/b;
  28. }
  29. }

现需要在每个方法调用前输出日志记录该方法的调用
**

反例1

在每个方法调用时执行逻辑之前和之后进行日志输出

  1. class Calculator{
  2. /**
  3. * 加
  4. */
  5. public int add(int a,int b){
  6. System.out.println("调用了add方法,参数是:a="+a+",b="+b);
  7. int sum = a+b;
  8. System.out.println("调用结束,返回结果是:"+sum);
  9. return sum;
  10. }
  11. /**
  12. * 减
  13. */
  14. public int sub(int a,int b) {
  15. System.out.println("调用了sub方法,参数是:a="+a+",b="+b);
  16. int t = a-b;
  17. System.out.println("调用结束,返回结果是:"+t);
  18. return t;
  19. }
  20. /**
  21. * 乘
  22. */
  23. public int mult(int a,int b){
  24. System.out.println("调用了mult方法,参数是:a="+a+",b="+b);
  25. int t = a*b;
  26. System.out.println("调用结束,返回结果是:"+t);
  27. return t;
  28. }
  29. /**
  30. * 除
  31. */
  32. public int div(int a,int b){
  33. System.out.println("调用了div方法,参数是:a="+a+",b="+b);
  34. int t = a/b;
  35. System.out.println("调用结束,返回结果是:"+t);
  36. return t;
  37. }
  38. }

如调用add方法:

  1. Calculator calculator = new Calculator();
  2. calculator.add(1, 2);

输出如下:

  1. 调用了add方法,参数是:a=1,b=2
  2. 调用结束,返回结果是:3

我们都知道,这种做法实际上并不合理,第一个是代码冗余重复,第二个是非业务逻辑和业务逻辑耦合严重,此时如果新增了一个方法,依旧要写这么多内容

反例2

利用模板方法模式,抽离共同内容

  1. class Calculator{
  2. /**
  3. * 执行前输出
  4. * @param methodName 方法名
  5. * @param params 参数列表
  6. */
  7. private void begin(String methodName,Object... params){
  8. System.out.println("调用了"+methodName+"方法,参数是:"+ Arrays.toString(params));
  9. }
  10. /**
  11. * 执行后输出
  12. * @param methodName 方法名
  13. * @param re 返回值
  14. */
  15. private void end(String methodName,Object re){
  16. System.out.println("调用"+methodName+"方法结束,返回结果是:"+re);
  17. }
  18. /**
  19. * 加
  20. */
  21. public int add(int a,int b){
  22. begin("add", a,b);
  23. int sum = a+b;
  24. end("add", sum);
  25. return sum;
  26. }
  27. /**
  28. * 减
  29. */
  30. public int sub(int a,int b) {
  31. begin("sub", a,b);
  32. int t = a-b;
  33. end("sub", t);
  34. return t;
  35. }
  36. /**
  37. * 乘
  38. */
  39. public int mult(int a,int b){
  40. begin("mult", a,b);
  41. int t = a*b;
  42. end("mult", t);
  43. return t;
  44. }
  45. /**
  46. * 除
  47. */
  48. public int div(int a,int b){
  49. begin("div", a,b);
  50. int t = a/b;
  51. end("div", t);
  52. return t;
  53. }
  54. }

此时调用add方法:

  1. Calculator calculator = new Calculator();
  2. calculator.add(1, 2);

输出如下:

  1. 调用了add方法,参数是:[1, 2]
  2. 调用add方法结束,返回结果是:3

此方法确实提高了代码重用性,但是依旧避免不了非业务代码和业务代码的耦合,不是我们想要的

正例(代理模式)

我们可以借助类的加载原理,动态代理Caculator的方法,也就是通过代理类调用Caculator方法,而不是直接执行Caculator的方法

首先改造Caculator为接口,因为动态代理只能代理接口**

  1. interface Calculator{
  2. int add(int a,int b);
  3. int sub(int a,int b);
  4. int mult(int a,int b);
  5. int div(int a,int b);
  6. }
  7. /**
  8. * 计算器
  9. */
  10. class CalculatorImpl implements Calculator{
  11. /**
  12. * 加
  13. */
  14. @Override
  15. public int add(int a,int b){
  16. int sum = a+b;
  17. return sum;
  18. }
  19. /**
  20. * 减
  21. */
  22. @Override
  23. public int sub(int a,int b) {
  24. int t = a-b;
  25. return t;
  26. }
  27. /**
  28. * 乘
  29. */
  30. @Override
  31. public int mult(int a,int b){
  32. int t = a*b;
  33. return t;
  34. }
  35. /**
  36. * 除
  37. */
  38. @Override
  39. public int div(int a,int b){
  40. int t = a/b;
  41. return t;
  42. }
  43. }

获取代理对象,其中的Positive是当前运行的对象

  1. //实例化一个对象
  2. Calculator calculator = new CalculatorImpl();
  3. //获取代理对象
  4. //Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
  5. Calculator proxyCaculator = (Calculator) Proxy.newProxyInstance(Positive.class.getClassLoader(), new Class[]{Calculator.class}, new MyHandler(calculator));

其中的MyHandler是一个处理类,实现InvocationHandler 接口,只有一个invoke方法,用于说明调用代理对象方法时执行的操作:

  1. class MyHandler implements InvocationHandler{
  2. private Calculator calculator;
  3. public MyHandler(Calculator calculator){
  4. this.calculator = calculator;
  5. }
  6. //所有调用代理对象的方法都会进入这个方法,而不是对应的方法
  7. @Override
  8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  9. System.out.println("调用了"+method.getName()+"方法,参数是:"+ Arrays.toString(args));
  10. Object re = method.invoke(calculator, args);
  11. System.out.println("调用"+method.getName()+"方法结束,返回结果是:"+re);
  12. return re;
  13. }
  14. }

此时通过代理类调用目标类的方法:

  1. proxyCaculator.add(1, 2);

输出如下:

  1. 调用了add方法,参数是:[1, 2]
  2. 调用add方法结束,返回结果是:3

关键点:**通过代理类调用目标类的方法**

应用场景

需要对类中的方法进行批量处理时

需要对类中的方法进行隐藏细节时

如:Spring AOP

优点

可以扩展目标对象的功能且不用改变目标对象代码,符合开闭原则

将客户端与目标对象份力,一定程度解耦

作为中介代理目标对象,一定程度保护了目标对象

缺点

增加系统复杂度

减缓访问速度

如有贻误,还请评论指正