乐趣区

关于动态代理:浅谈JDK动态代理转载

一个小需要: 给原有办法增加日志打印

假如当初咱们有一个类 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;
    }

    // 乘法、除法...
}

现有一个需要:在每个办法执行前后打印日志。你有什么好的计划?

间接批改

很多人最直观的想法是间接批改 Calculator 类:

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 花十分钟删除日志打印的代码!

所以,此种计划 PASS!


动态代理实现日志打印

“动态代理”四个字蕴含了两个概念:动态、代理。咱们先来理解什么叫“代理”,至于何为“动态”,须要和“动静”比照着讲。

代理是一种模式,提供了对指标对象的间接拜访形式,即通过代理拜访指标对象。如此便于在指标实现的根底上减少额定的性能操作,前拦挡,后拦挡等,以满足本身的业务需要。
援用博客:Java 动态代理和动静代理 – 纪煜楷 – 博客园

援用自:Java 动态代理和动静代理 – 纪煜楷 – 博客园

罕用的代理形式能够粗分为:动态代理和动静代理。

动态代理的实现比较简单:编写一个代理类,实现与指标对象雷同的接口,并在外部保护一个指标对象的援用。通过结构器塞入指标对象,在代理对象中调用指标对象的同名办法,并增加前拦挡,后拦挡等所需的业务性能。

按下面的形容,代理类和指标类须要实现同一个接口,所以我打算这样做:

  • 将 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);
    }
}  

动态代理示意图

动态代理的长处:能够在不批改指标对象的前提下,对指标对象进行性能的扩大和拦挡。然而它也仅仅解决了上一种计划 4 大毛病中的第 1 点:

  1. 间接批改源程序,不合乎开闭准则。应该对扩大凋谢,对批改敞开 √
  2. 如果 Calculator 有几十个、上百个办法,批改量太大 ×
  3. 存在反复代码(都是在外围代码前后打印日志)×
  4. 日志打印硬编码在代理类中,不利于前期保护:比方你花了一上午终于写完了,组长通知你这个性能勾销,于是你又要关上 Calculator 花十分钟删除全副新增代码!×
    • *

动态代理的问题

下面案例中,代理类是咱们当时编写的,而且要和指标对象类实现雷同接口。因为 CalculatorImpl(指标对象)须要日志性能,咱们即编写了 CalculatorProxy(代理对象),并通过结构器传入 CalculatorImpl(指标对象),调用指标对象同名办法的同时增加加强代码。

然而这里有个问题!代理对象结构器的参数类型是 Calculator,这意味着它只能承受 Calculator 的实现类对象,亦即咱们写的代理类 CalculatorProxy 只能给 Calculator 做代理,它们绑定死了!

如果当初咱们零碎须要全面革新,给其余类也增加日志打印性能,就得为其余几百个接口都各自写一份代理类 …

本人手动写一个类并实现接口切实太麻烦了。认真一想, 咱们其实想要的并不是代理类,而是代理对象! 那么,是否让 JVM 依据接口主动生成代理对象呢?

比方,有没有一个办法,我传入接口,它就给我主动返回代理对象呢?

答案是必定的。

退出移动版