java核心技术动态代理的原理(代码片段)

sysu_lluozh sysu_lluozh     2022-12-28     310

关键词:

编程语言通常有各种不同的分类角度,动态类型和静态类型就是其中一种分类角度,简单区分就是语言类型信息是在运行时检查,还是编译期检查

与其近似的还有一个对比,就是所谓强类型和弱类型,就是不同类型变量赋值时,是否需要显式地(强制)进行类型转换

那么,如何分类Java语言呢?通常认为,Java是静态的强类型语言,但是因为提供了类似反射等机制,也具备了部分动态类型语言的能力

言归正传,接下来的问题是:

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

一、典型回答

1.1 反射机制

反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力

通过反射可以直接操作类或者对象,比如

  • 获取某个对象的类定义
  • 获取类声明的属性和方法
  • 调用方法或者构造对象
  • 运行时修改类定义

1.2 动态代理

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

动态代理,就是通过同性质的中间人去处理请求,同时也被代理对象赋予了操作权限的一种存在

实现动态代理的方式很多,比如:

  • JDK自身提供的动态代理,主要利用了上面提到的反射机制
  • 利用更高性能的字节码操作机制(类似ASM、cglib(基于ASM)、Javassist等),使用其他的实现方式

二、考点分析

关于Java反射机制及动态代理的原理,下意识地以为动态代理就是利用反射机制实现的,这么说也不算错但稍微有些不全面。功能才是目的,实现的方法有很多

总的来说,这主要考察的是Java语言的另外一种基础机制:反射

  • 反射

反射就像是一种魔法,引入运行时自省能力,赋予了Java语言令人意外的活力,通过运行时操作元数据或对象,Java可以灵活地操作运行时才能确定的信息

  • 动态代理

动态代理则是延伸出来的一种广泛应用于产品开发中的技术,很多繁琐的重复编程,都可以被动态代理机制优雅地解决

从考察知识点的角度,这道题涉及的知识点:

  • 考察对反射机制的了解和掌握程度
  • 动态代理解决了什么问题,在业务系统中的应用场景是什么?
  • JDK动态代理在设计和实现上与cglib等方式有什么不同,进而如何取舍?

三、知识扩展

3.1 反射机制及其演进

对于Java语言的反射机制本身,查看java.langjava.lang.reflect包下的相关抽象,可以有一个很直观的印象。ClassFieldMethodConstructor等完全就是去操作类和对象的元数据对应。需要掌握基本场景编程,可以官方提供的参考文档

关于反射,有一点需要特别注意,就是反射提供的AccessibleObject.setAccessible​(boolean flag),它的子类也大都重写了这个方法,这里的accessible可以理解成修饰成员的publicprotectedprivate,这意味着可以在运行时修改成员访问限制

setAccessible的应用场景非常普遍,遍布日常开发、测试、依赖注入等各种框架中。比如,在 O/R Mapping框架中,为一个Java实体对象运行时自动生成settergetter的逻辑,这是加载或者持久化数据非常必要的,框架通常可以利用反射做这个事情,而不需要开发者手动写类似的重复代码

另一个典型场景就是绕过API访问控制。日常开发时可能被迫要调用内部API去做些事情,比如,自定义的高性能NIO框架需要显式地释放DirectBuffer,使用反射绕开限制是一种常见办法

3.2 动态代理

前面的问题问到了动态代理,接下来看看它到底是解决什么问题?

首先,它是一个代理机制
如果熟悉设计模式中的代理模式,可以知道代理可以看作是对调用目标的一个包装,这样对目标代码的调用不是直接发生的,而是通过代理完成
其实很多动态代理场景,认为也可以看作是装饰器(Decorator)模式的应用

通过代理可以让调用者与实现者之间解耦
比如进行RPC调用,框架内部的寻址、序列化、反序列化等,对于调用者往往是没有太大意义的,通过代理可以提供更加友善的界面

代理的发展经历了静态到动态的过程,源于静态代理引入的额外工作
类似早期的RMI之类古董技术,还需要rmic之类工具生成静态stub等各种文件,增加了很多繁琐的准备工作,而这又和业务逻辑没有关系
利用动态代理机制,相应的stub等类可以在运行时生成,对应的调用操作也是动态完成,极大地提高了生产力。改进后的RMI已经不再需要手动去准备这些了,虽然它仍然是相对古老落后的技术,未来也许会逐步被移除

可以看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;
   


上面的JDK Proxy例子,非常简单地实现了动态代理的构建和代理操作

  • 首先,实现对应的InvocationHandler
  • 然后,以接口Hello为纽带,为被调用目标构建代理对象
  • 进而应用程序就可以使用代理对象间接运行调用目标的逻辑,代理为应用插入额外逻辑(比如println)提供了便利的入口

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

JDK Proxy是实现InvocationHandler,使用invoke方法进行调用,实质上使用了JDK的反射原理 Cglib使用的是继承目标类,相当于代理类是子类代理类父类是Proxy类,通过jdk代理生成的类都继承Proxy类: 且因为Java是单继承的,而代理类又必须继承自Proxy类,所以通过jdk代理的类必须实现接口

如果被调用者没有实现接口,但还是希望利用动态代理机制,那么可以考虑其他方式
Spring AOP支持两种模式的动态代理,JDK Proxy或者cglib,如果选择cglib方式会发现对接口的依赖被克服了

动态代理应用非常广泛,虽然最初多是因为RPC等使用,但是动态代理的使用场景远远不仅如此,它完美符合Spring AOP等切面编程
AOP简单来说它可以看作是对OOP的一个补充,因为OOP对于跨越不同对象或类的分散、纠缠逻辑表现力不够,比如在不同模块的特定阶段做一些事情,类似日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等,可以参考下面这张图


AOP通过(动态)代理机制可以让开发者从这些繁琐事项中抽身出来,大幅度提高了代码的抽象程度和复用度。从逻辑上来说,在软件设计和实现中的类似代理,如Facade、Observer等很多设计目的,都可以通过动态代理优雅地实现

四、补充说明

4.1 反射

反射最大的作用之一在于可以不在编译时知道某个对象的类型,而在运行时通过提供完整的"包名+类名.class"得到

注:不是在编译时,而是在运行时

4.1.1 功能

  • 在运行时能判断任意一个对象所属的类
  • 在运行时能构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法

直白一些就是,利用Java反射机制可以加载一个运行时才得知名称的class,获悉其构造方法,并生成其对象实体,能对其fields设值并唤起其methods

4.1.2 应用场景

反射技术常用在各类通用框架开发中
因为为了保证框架的通用性,需要根据配置文件加载不同的对象或类,并调用不同的方法,这个时候就会用到反射——运行时动态加载需要加载的对象

4.1.3 特点

由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射
另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题

4.2 动态代理

为其他对象提供一种代理以控制对这个对象的访问
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在两者之间起到中介的作用(可类比房屋中介,房东委托中介销售房屋、签订合同等)

所谓动态代理,就是实现阶段不用关心代理谁,而是在运行阶段才指定代理哪个一个对象(不确定性)。如果是自己写代理类的方式就是静态代理(确定性)

4.2.1 组成要素

动态代理模式主要涉及三个要素:

  • 其一:抽象类接口
  • 其二:被代理类(具体实现抽象接口的类)
  • 其三:动态代理类(实际调用被代理类的方法和属性的类)

4.2.2 实现方式

实现动态代理的方式很多,比如:
JDK自身提供的动态代理,就是主要利用了反射机制
ASM、CGLIB(基于ASM)、Javassist等,利用字节码操作机制

举例,常可采用的JDK提供的动态代理接口InvocationHandler来实现动态代理类
其中:

  • invoke方法是该接口定义必须实现的,它完成对真实方法的调用
  • 通过InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务
  • 可以在invoke方法实现中增加自定义的逻辑实现,实现对被代理类的业务逻辑无侵入

五、小结

简要回顾了反射机制,以及反射在Java语言演进中正在发生的变化,并且进一步探讨了动态代理机制和相关的切面编程,分析了其解决的问题

java动态代理的底层原理(代码片段)

点击关注公众号,实用技术文章及时了解1.代理模式代理模式是常用的设计模式之一,其特征是代理类与被代理类有相同的接口,代理类可以为被代理类方法执行进行前置后置处理,增强被代理类方法代理模式的... 查看详情

java基础干货动态代理核心代码(代码片段)

JDK动态代理//自定义的接口类,JDK动态代理的实现必须有对应的接口类publicinterfaceExInterfacevoidexecute();//A类,实现了ExInterface接口类publicclassAimplementsExInterfacepublicvoidexecute()System.out.println("执行A的exec 查看详情

jav设计模之的动态代理(代码片段)

...就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是java的动态代理机制,所以本篇随笔就是... 查看详情

java技术专题「源码分析系列」深入分析jdk动态代理的分析原理机制(代码片段)

JDK动态代理到底是怎么实现?JDK的动态代理的类看不见摸不着,虽然可以看到效果,但是底层到底是怎么做的,为什么要求实现接口呢?从Proxy.newProxyInstance入手publicstaticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]in... 查看详情

cglib动态代理实现及原理(代码片段)

...理呢,这就需要Cglib了。Cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与Cglib动态代理... 查看详情

cglib动态代理实现及原理(代码片段)

...理呢,这就需要Cglib了。Cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与Cglib动态代理... 查看详情

cglib动态代理实现及其原理浅析(代码片段)

...理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。举例:1、定义被代理类... 查看详情

android插件化原理解析——hook机制之动态代理(代码片段)

提示:Java的动态代理非常有用,特别实在Android逆向过程中非常有用,比较典型的两个应用实例就是:1.xposed框架利用Java的动态代理Hook实现对Https请求信息的打印替代Https抓包的操作。2.利用Java的动态代理Hook实现... 查看详情

java动态代理demo实现原理参数理解(代码片段)

  动态代理两个重要的组成:  Proxy  与  InvocationHandlerHello1hello=(Hello1)Proxy.newProxyInstance(Hello.class.getClassLoader(),newClass[]Hello1.class,newHelloProxy<Hel 查看详情

动态代理(代码片段)

动态代理:是使用反射和字节码的技术,在运行期创建指定接口或类的子类(动态代理)以及其实例对象的技术,通过这个技术可以无侵入性的为代码进行增强;Java的动态代理技术实现主要有两种方式:1.JDK原生动态代理2.CGLIB动态代理&n... 查看详情

动态代理-rpc实现核心原理(代码片段)

...码前提下,还能实现非业务逻辑跟业务逻辑的解耦。核心就是动态代理,通过对字节码进行增强,在方法调用时进行拦截,以便于在方法调用前后,增加处理逻辑。1远程调用的魔法使用RPC,一般先找服务... 查看详情

java:jdk动态代理技术解析(代码片段)

当前版本jdk1.81.声明当前内容主要为解析jdk的动态代理技术,当前内容参考jdk源码2.创建jdk方式解析基本的jdk动态代理创建方法如下:publicstaticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandlerh)一般是需要... 查看详情

jvm原理(36)透过字节码生成审视java动态代理运作机制(代码片段)

我们在使用spring这类框架的时候,基于动态代理的使用,比如AOP,会使得开发更加灵活,那么在字节码的层面动态代理是什么样子的呢,生成出来的代理类结构是什么,本次我们首先写一个动态代理的例子... 查看详情

jdk动态代理运行原理(代码片段)

JDK动态代理运行原理程序演示源码讲解总结这几天有空研究了下JDk的动态代理,JDK的动态代理类都在java.lang.reflect包下,写了一些小程序来演示了相关类的使用,同时做了一些与CGLIb的对比,以后有空再讲述下lombok... 查看详情

springaop底层原理(动态代理)-》aop概念及术语-》aop实现(代码片段)

...复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。二、AOP底层... 查看详情

jdk和cglib动态代理原理(代码片段)

...我的的Github:https://github.com/h2pl/  AOP的基础是Java动态代理,了解和使用两种动态代理能让我们更好地理解AOP,在讲解AOP之前,让我们先来看看Java动态代理的使用方式以及底层实现原理。转自https://www.jianshu.com/u/668d0795a95b... 查看详情

cglib动态代理实现及其原理浅析(代码片段)

...理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。举例:1、定义被代理类... 查看详情

设计模式-动态代理原理及模仿jdkproxy写一个属于自己的动态代理(代码片段)

...糙,大家可以选择性阅读。本篇文章的目的是简单的分析动态代理的原理及模仿JDKProxy手写一个动态代理以及对几种代理做一个总结。对于代理模式的介绍和讲解,网上已经有很多优质的文章,我这里就不会再过多的介绍了,这... 查看详情