反射和动态代理

127 阅读2分钟

什么是反射

在运行时,对于任意的一个类,

  • 都能够知道这个类的所有属性和方法
  • 对任意一个对象都能够通过反射机制调用一个类的任意方法

这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制

反射的使用

  1. object.getClass(),这个是Object类里面的方法
  2. Object.Class,任何的数据类型,基本数据类型或者抽象数据类型,都可以通过这种方式获取类
  3. Class.forName(""),Class类提供了这样一个方法,让我们通过类名来获取到对象类

使用反射创建实例,两个要求

  • 类必须有一个无参数的构造器
  • 类的构造器的访问权限需要足够

getDeclaredXXX获取所有,getXXX获取公共

method.invoke()

method-invoke-test.png

invoke方法.png

acquireMethodAccessor.png

newMethodAccessor.png

Class Dagram

MethodAccessor.png GenerateMethodAccessor1是通过ASM字节码技术运行时生成的实现类

DelegatingMethodAccessorImpl.png

NativeMethodAccessorImpl.png

GeneratedMethodAccessor1.png

执行过程

MethodAccessor执行过程.png

DelegatingMethodAccessorImpl实现类中有MethodAccessor的引用,初始时该引用指向了NativeMethodAccessorImpl的实例,通过native的inkove方法完成方法调用

noInflation和inflationThreshold

inflation是膨胀的意思,在ectionFactory类中,有2个重要的字段:noInflation(默认false)和inflationThreshold(默认15)

按照默认值来说,当反射调用同一个方法的前15次都是通过JNI获取该方法的字节码并调用方法,当执行次数大于15次的时候,会生成一个GeneratedMethodAccessor1实例,覆盖DelegatingMethodAccessorImpl中的MethodAccessor,通过java调用对应的方法

checkInitted方法中可以通过-Dsun.reflect.inflationThreshold=xxx-Dsun.reflect.noInflation=true对这两个字段重新设置,而且只会设置一次

要想关闭膨胀,可以把noInflation设置为true

什么是动态代理

使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强

java源文件编译生成字节码.webp

动态代理生成字节码.webp

JDK动态代理

动态代理只能对接口中声明的方法进行代理

角色

  • Proxy:java.lang.reflect.Proxy是所有动态代理的父类。它通过静态方法newProxyInstance()来创建动态代理的class对象和实例
  • InvocationHandler:每一个动态代理实例都有一个关联的InvocationHandler。通过代理实例调用方法,方法调用请求会被转发给InvocationHandler的invoke方法

实现原理

newProxyInstance.png

Proxy内部类生成class.png

$Proxy0.png

CGLIB动态代理

CGLib(Code Generation Library)是一个基于ASM的字节码生成库。它允许我们在运行时对字节码进行修改或动态生成。CGLib通过继承被代理类的方式实现代理

角色

Enhancer:Enhancer指定要代理的目标对象。通过create方法得到代理对象。通过代理实例调用非final方法,方法调用请求会首先转发给MethodInterceptor的intercept

MethodInterceptor:通过代理实例调用方法,调用请求都会转发给intercept方法进行增强