zoukankan      html  css  js  c++  java
  • 动态代理原理剖析

    动态代理的常用实现方式是反射。反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。

    动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)。

    JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。但动态代理不止有反射一种实现方式,还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM,一个 Java 字节码操作框架)、Javassist 等。简单来说,动态代理是一种行为方式,而反射或 ASM 只是它的一种实现手段而已。

    JDK Proxy 和 CGLib 的区别主要体现在以下几个方面:

    • JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
    • Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
    • JDK Proxy 是通过拦截器加反射的方式实现的;
    • JDK Proxy 只能代理继承接口的类;
    • JDK Proxy 实现和调用起来比较简单;
    • CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
    • CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。

    什么是静态代理

    静态代理是代理类在编译期间就创建好了,不是编译器生成的代理类,而是手动创建的类。在编译时就已经将接口,被代理类,代理类等确定下来。,软件设计中所指的代理一般是指静态代理,也就是在代码中显式指定的代理。

    下面我们通过一个简单的案例,来了解下静态代理。

    Cat.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * 静态代理类接口, 委托类和代理类都需要实现的接口规范。
    * 定义了一个猫科动物的两个行为接口,吃东西,奔跑。
    * 作为代理类 和委托类之间的约束接口
    */
    public interface Cat {
    public String eatFood(String foodName);
    public boolean running();
    }

    Lion.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /**
    * 狮子 实现了猫科动物接口Cat, 并实现了具体的行为。作为委托类实现
    */
    public class Lion implements Cat {
    private String name;
    private int runningSpeed;
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public int getRunningSpeed() {
    return runningSpeed;
    }
    public void setRunningSpeed(int runningSpeed) {
    this.runningSpeed = runningSpeed;
    }
    public Lion() { }

    @Override
    public String eatFood(String foodName) {
    String eat = this.name + " Lion eat food. foodName = " + foodName;
    System.out.println(eat); return eat;
    }
    @Override
    public boolean running() {
    System.out.println(this.name + " Lion is running . Speed :" + this.runningSpeed); return false;
    }
    }

    代理类角色(FeederProxy)

    FeederProxy.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * 饲养员 实现Cat接口,作为静态代理类实现。代理狮子的行为。
    * 代理类中可以新增一些其他行为,在实践中主要做的是参数校验的功能。
    */
    public class FeederProxy implements Cat {
    private Cat cat;
    public FeederProxy(){}
    public FeederProxy(Cat cat) {
    if (cat instanceof Cat) {
    this.cat = cat;
    }
    }
    public void setCat(Cat cat) {
    if (cat instanceof Cat) {
    this.cat = cat;
    }
    }
    @Override
    public String eatFood(String foodName) {
    System.out.println("proxy Lion exec eatFood ");
    return cat.eatFood(foodName);
    }
    @Override
    public boolean running() {
    System.out.println("proxy Lion exec running.");
    return cat.running();
    }
    }

    静态代理类测试

    staticProxyTest.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 静态代理类测试
    */
    public class staticProxyTest {
    public static void main(String[] args) {
    Lion lion = new Lion();
    lion.setName("狮子 小王");
    lion.setRunningSpeed(100);
    /**
    * new 静态代理类,静态代理类在编译前已经创建好了,和动态代理的最大区别点
    */
    Cat proxy = new FeederProxy(lion);
    System.out.println(Thread.currentThread().getName()+" -- " + proxy.eatFood("水牛"));
    proxy.running();
    }
    }

    静态代理很好的诠释了代理设计模式,代理模式最主要的就是有一个公共接口(Cat),一个委托类(Lion),一个代理类(FeederProxy),代理类持有委托类的实例,代为执行具体类实例方法。

    代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指客户端不直接调用实际对象的方法,客户端依赖公共接口并使用代理类。 那么我们在代理过程中就可以加上一些其他用途。

    就这个例子来说在 eatFood 方法调用中,代理类在调用具体实现类之前添加System.out.println(“proxy Lion exec eatFood “);语句 就是添加间接性带来的收益。代理类存在的意义是为了增加一些公共的逻辑代码。

    静态代理的缺陷

    1. 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

    2. 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

    3. 静态代理一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。

    JDK Proxy 和 CGLib 的使用样例

    JDK Proxy 动态代理实现

  • 相关阅读:
    Python for Infomatics 第14章 数据库和SQL应用四(译)
    展望2017
    bing的简单英文字典工具
    自我安慰
    Python for Infomatics 第14章 数据库和SQL应用三(译)
    Python for Infomatics 第14章 数据库和SQL应用二(译)
    Python for Infomatics 第14章 数据库和SQL应用一(译)
    希望父亲早日恢复
    Python for Infomatics 第13章 网页服务四(译)
    Python for Infomatics 第13章 网页服务三(译)
  • 原文地址:https://www.cnblogs.com/hulianwangjiagoushi/p/13038776.html
Copyright © 2011-2022 走看看