美文网首页
第6讲 | 动态代理是基于什么原理?

第6讲 | 动态代理是基于什么原理?

作者: governlee | 来源:发表于2019-04-17 23:53 被阅读0次

    谈谈 Java 反射机制,动态代理是基于什么原理?

    典型回答

    反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect)的能力,通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。通过运行时操作元数据或对象,Java 可以灵活地操作运行时才能确定的信息。

    动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制。

    实现动态代理的方式很多,比如 JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。还有利用更高性能的字节码操作机制,类似 ASM、cglib(基于ASM)、Javassist 等。很多繁琐的重复编程,都可以被动态代理机制优雅地解决。

    反射机制及其演进

    查看java.lang或java.lang.reflect包下的相关抽象,Class、Field、Method、Constructor等,这些完全就是我们去操作类和对象的元数据对应。

    AccessibleObject.setAccessible(boolean)可以运行时修改成员的访问权限。

    但是,在 Java 9 以后,这个方法的使用可能会存在一些争议。因为 Jigsaw 项目新增的模块化系统,出于强封装性的考虑,对反射访问进行了限制。Jigsaw 引入了所谓 Open 的概念,只有当被反射操作的模块和指定的包对反射调用者模块Open,才能使用 setAccessible;否则,被认为不合法操作。如果我们的实体类是定义在模块里面,我们需要在模块描述符中明确声明:

    module MyEntities {
        // Open for reflection
        opens com.mycorp to java.persistence;
    }
    

    动态代理

    首先,它是一个代理机制。很多动态代理场景,可以看作是装饰器(Decorator)模式的应用。通过代理可以让调用者与实现者之间解耦

    代理的发展经历了静态到动态的过程,源于静态代理引入的额外工作。

    JDK 动态代理的一个简单例子。下面只是加了一句 print,在生产系统中,我们可以轻松扩展类似逻辑进行诊断、限流等。

    public class MyDynamicProxy {
        public static  void main (String[] args) {
            HelloImpl hello = new HelloImpl();
            MyInvocationHandler handler = new MyInvocationHandler(hello);
            // 构造代码实例
            Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);
            // 调用代理方法
            proxyHello.sayHello();
        }
    }
    interface Hello {
        void sayHello();
    }
    class HelloImpl implements  Hello {
        @Override
        public void sayHello() {
            System.out.println("Hello World");
        }
    }
     class MyInvocationHandler implements InvocationHandler {
        private Object target;
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("Invoking sayHello");
            Object result = method.invoke(target, args);
            return result;
        }
    }
    

    从 API 设计和实现的角度,这种实现仍然有局限性,因为它是以接口为中心的,相当于添加了一种对于被调用者没有太大意义的限制。我们实例化的是 Proxy 对象,而不是真正的被调用类型,这在实践中还是可能带来各种不便和能力退化。

    如果被调用者没有实现接口,可以使用cglib方式。

    Spring AOP 支持两种模式的动态代理,JDK Proxy或者cglib

    cglib 动态代理采取的是创建目标类的子类的方式,因为是子类化,我们可以达到近似使用被调用者本身的效果。

    JDK Proxy 的优势:

    1. 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
    2. 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版jdk中能使用。
    3. 代码实现简单。

    基于类似 cglib 框架的优势:

    1. 有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践。
    2. 只操作我们关心的类,而不必为其他相关类增加工作量。
    3. 高性能

    动态代理应用非常广泛,它完美符合 Spring AOP 等切面编程。可以看作是对 OOP 的一个补充,因为 OOP 对于跨越不同对象或类的分散、纠缠逻辑表现力不够,比如在不同模块的特定阶段做一些事情,类似日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。

    AOP 通过(动态)代理机制可以让开发者从这些繁琐事项中抽身出来,大幅度提高了代码的抽象程度和复用度。

    相关文章

      网友评论

          本文标题:第6讲 | 动态代理是基于什么原理?

          本文链接:https://www.haomeiwen.com/subject/scpowqtx.html