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 动态代理实现

  • 相关阅读:
    《Linux设备驱动开发详解(第2版)》配套视频登录51cto教育频道
    异常Address already in use: JVM_Bind的处理
    你的Jsp页面有黄×么,有黄色问号么?Multiple annotations found at this line:
    dispatch_get_current_queue 废弃
    二叉树代码(较全)
    ArrayList and LinkedList
    android的tabhost+RadioGroup+PopupWindow
    子进程继承父进程的当前工作目录的问题
    oracle AWR深入研究分析,如何使用
    Linux下对后台进程通过kill传递信号不起作用的问题
  • 原文地址:https://www.cnblogs.com/hulianwangjiagoushi/p/13038776.html
Copyright © 2011-2022 走看看