关于动态代理:浅谈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依据接口主动生成代理对象呢?

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

答案是必定的。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理