Objenesis 底层探究

Objenesis 简介

Objenesis 是一个 Java 库,用于在不调用构造方法的情况下创建对象。由于绕过了构造方法,所以无法调用构造方法中的初始化逻辑。相应的,Objenesis 无法创建抽象类、枚举、接口的实例对象。

起源

与其称之为起源,不如说这是 Java 中反射创建对象的一种应用,先来看看 java.lang.reflect.Constructor 中的一段代码:

    @CallerSensitive
    @ForceInline // to ensure Reflection.getCallerClass optimization
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, clazz, modifiers);
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

可以看到,当我们通过获取 Constructor 对象,反射创建实例对象时,实际上是通过ConstructorAccessor 中的 newInstance 方法来创建对象。这是一个 JDK 内部接口,全名是jdk.internal.reflect.ConstructorAccessor。

此处主要关注它的一个子类:jdk.internal.reflect.SerializationConstructorAccessorImpl,一个内部抽象类。

这些类都是一些特殊的类,可以被 JVM 识别,来进入其它类进行操作,而不受语言限制。

而 Objenesis 的核心,就是通过动态生成 ConstructorAccessor,来绕过原有 ConstructorAccessor 对象,调用 newInstance 方法,实现实例化。

核心

ObjenesisBase,调用入口,通过不同策略来实例化对象:

   public <T> T newInstance(Class<T> clazz) {
      return getInstantiatorOf(clazz).newInstance();
   }

标准实现,采用的策略为 StdInstantiatorStrategy:

   public <T> ObjectInstantiator<T> newInstantiatorOf(Class<T> type) {

      if(PlatformDescription.isThisJVM(HOTSPOT) || PlatformDescription.isThisJVM(OPENJDK)) {
         // Java 7 GAE was under a security manager so we use a degraded system
         if(PlatformDescription.isGoogleAppEngine() && PlatformDescription.SPECIFICATION_VERSION.equals("1.7")) {
            if(Serializable.class.isAssignableFrom(type)) {
               return new ObjectInputStreamInstantiator<>(type);
            }
            return new AccessibleInstantiator<>(type);
         }
         // The UnsafeFactoryInstantiator would also work. But according to benchmarks, it is 2.5
         // times slower. So I prefer to use this one
         return new SunReflectionFactoryInstantiator<>(type);
      }
      else if(PlatformDescription.isThisJVM(DALVIK)) {
         if(PlatformDescription.isAndroidOpenJDK()) {
            // Starting at Android N which is based on OpenJDK
            return new UnsafeFactoryInstantiator<>(type);
         }
         if(ANDROID_VERSION <= 10) {
            // Android 2.3 Gingerbread and lower
            return new Android10Instantiator<>(type);
         }
         if(ANDROID_VERSION <= 17) {
            // Android 3.0 Honeycomb to 4.2 Jelly Bean
            return new Android17Instantiator<>(type);
         }
         // Android 4.3 until Android N
         return new Android18Instantiator<>(type);
      }
      else if(PlatformDescription.isThisJVM(GNU)) {
         return new GCJInstantiator<>(type);
      }
      else if(PlatformDescription.isThisJVM(PERC)) {
         return new PercInstantiator<>(type);
      }

      // Fallback instantiator, should work with most modern JVM
      return new UnsafeFactoryInstantiator<>(type);

   }

主要是针对不同平台,调用不同的类,此处只关注 SunReflectionFactoryInstantiator:

   public SunReflectionFactoryInstantiator(Class<T> type) {
      Constructor<Object> javaLangObjectConstructor = getJavaLangObjectConstructor();
      mungedConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(
          type, javaLangObjectConstructor);
      mungedConstructor.setAccessible(true);
   }

   public T newInstance() {
      try {
         return mungedConstructor.newInstance((Object[]) null);
      }
      catch(Exception e) {
         throw new ObjenesisException(e);
      }
   }

可以看到,就是通过这个动态生成的 mungedConstructor 来创建对象。

下面来看看这个 mungedConstructor 是如何生成的?

   public static <T> Constructor<T> newConstructorForSerialization(Class<T> type,
      Constructor<?> constructor) {
      Class<?> reflectionFactoryClass = getReflectionFactoryClass();
      Object reflectionFactory = createReflectionFactory(reflectionFactoryClass);

      Method newConstructorForSerializationMethod = getNewConstructorForSerializationMethod(
         reflectionFactoryClass);

      try {
         return (Constructor<T>) newConstructorForSerializationMethod.invoke(
            reflectionFactory, type, constructor);
      }
      catch(IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
         throw new ObjenesisException(e);
      }
   }

通过上文知道,ConstructorAccessor 这些 JDK 内部类,由于安全考虑,外部无法直接使用,但是 JDK 又留了特殊的入口供开发者使用,这个入口就是:sun.reflect.ReflectionFactory。

    public Constructor<?> newConstructorForSerialization(Class<?> cl,
                                                         Constructor<?> constructorToCall)
    {
        return delegate.newConstructorForSerialization(cl,
                                                       constructorToCall);
    }

而这个 delegate 就是 jdk.internal.reflect.ReflectionFactory:

    public final Constructor<?> newConstructorForSerialization(Class<?> cl,
                                                               Constructor<?> constructorToCall)
    {
        if (constructorToCall.getDeclaringClass() == cl) {
            constructorToCall.setAccessible(true);
            return constructorToCall;
        }
        return generateConstructor(cl, constructorToCall);
    }
    private final Constructor<?> generateConstructor(Class<?> cl,
                                                     Constructor<?> constructorToCall) {


        ConstructorAccessor acc = new MethodAccessorGenerator().
            generateSerializationConstructor(cl,
                                             constructorToCall.getParameterTypes(),
                                             constructorToCall.getExceptionTypes(),
                                             constructorToCall.getModifiers(),
                                             constructorToCall.getDeclaringClass());
        Constructor<?> c = newConstructor(constructorToCall.getDeclaringClass(),
                                          constructorToCall.getParameterTypes(),
                                          constructorToCall.getExceptionTypes(),
                                          constructorToCall.getModifiers(),
                                          langReflectAccess().
                                          getConstructorSlot(constructorToCall),
                                          langReflectAccess().
                                          getConstructorSignature(constructorToCall),
                                          langReflectAccess().
                                          getConstructorAnnotations(constructorToCall),
                                          langReflectAccess().
                                          getConstructorParameterAnnotations(constructorToCall));
        setConstructorAccessor(c, acc);
        c.setAccessible(true);
        return c;
    }

可以看到,最终是通过 jdk.internal.reflect.MethodAccessorGenerator 来动态的生成了 ConstructorAccessor。顺便说一下,具体的生成是采用了 JDK 内部的 ASM 操作类(jdk.internal.reflect.ClassFileAssembler)生成类的字节码,然后加载字节码,得到 Class 对象,调用 Class#newInstance 方法,得到了这个动态生成的 ConstructorAccessor 的具体对象。

下面来看看这个动态生成的 ConstructorAccessor 具体是什么。

动态 ConstructorAccessor 实现类

通读了生成这个类的字节码操作代码,翻译出的伪代码如下:

public jdk.internal.reflect.GeneratedSerializationConstructorAccessor1 extends jdk.internal.reflect.SerializationConstructorAccessorImpl {
	public GeneratedSerializationConstructorAccessor1 () {
		this.super();
	}
	public Object newInstance(Object[] args) {
		try {
		// new targetClass
		// 校验参数	
		// 1. args 为 null,跳过校验
		// 2. args 不为 null,校验参数数组长度和参数类型数组长度是否一致
		} catch (NullPointerException | ClassCastException var) {
			throw new IllegalArgumentException(var.toString());
		}
		
		try {
			// 调用 constructorToCall 的构造方法,此处由于传入 Object,即调用 Object 的无参构造
		}
		catch (Throwable var) {
			throw new InvocationTargetException(var);
		}
		
	}
}

应用

Spring 集成了Objenesis,作为在 AOP 时采用 Cglib 代理生成 Class 类后的实例化操作。

默认采用的实例化策略就是StdInstantiatorStrategy。这样,就避开了目标类的构造方法,实现了采用 Cglib 实现 AOP 时通过 ASM 动态生成的 proxyClass 的实例化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/585453.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【笔试训练】day15

1.平方数 水题直接看代码 代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include<math.h> #include<algorithm> using namespace std; typedef long long ll; int main() {ll x;cin >> x;ll a sqrt(x);if (abs(a * a -…

【Unity动画系统】动画状态转换详解

动画状态转换 此空处可以改换新转换名字。 表示有多个转换&#xff0c;播放顺序不可调整。 Solo:表示只执行它们&#xff0c;其他没勾选的不考虑&#xff1b;都勾选了&#xff0c;哪个转换条件先满足&#xff0c;就先执行哪个转换;如果同时满足&#xff0c;那就按顺序执行。 M…

【数据结构】顺序表专题

前言 本篇文章我们来进行有关顺序表的专题训练&#xff0c;让我们一起来看一下有关顺序表的算法题 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;数据结构 &#x1f4dd;若有问题 评论区见 &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 1.移除…

Python urllib 爬虫入门(1)

本文主要为Python urllib类库函数和属性介绍及一些简单示例。 目录 urllib爬取网页 简单示例 写入文件 其他读取方法 readline函数 readlines函数 response属性 当前环境信息 返回状态码 返回url地址 对url进行编码与解码 写入文件 总结 urllib爬取网页 通过pyth…

牛客网刷题 | CC1 获取字符串长度

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 键盘输入一个字符串…

Leetcode297_二叉树的序列化与反序列化

1.leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xf…

redis故障中出现的缓存击穿、缓存穿透、缓存雪崩?

一、背景&#xff1a; 在维护redis服务过程中&#xff0c;经常遇见一些redis的名词&#xff0c;例如缓存击穿、缓存穿透、缓存雪崩等&#xff0c;但是不是很理解这些&#xff0c;如下就来解析一下缓存击穿、缓存穿透、缓存雪崩名词。 二、缓存穿透问题&#xff1a; 常见的缓存使…

RTMP 直播推流 Demo(一)—— 项目配置与视频预览

音视频编解码系列目录&#xff1a; Android 音视频基础知识 Android 音视频播放器 Demo&#xff08;一&#xff09;—— 视频解码与渲染 Android 音视频播放器 Demo&#xff08;二&#xff09;—— 音频解码与音视频同步 RTMP 直播推流 Demo&#xff08;一&#xff09;—— 项目…

使用JNI机制加载本地方法的小案例

JNI 最近在学习Android&#xff0c;其中需要使用到c的库&#xff0c;这个时候就要使用到JNI机制了&#xff0c;简单来说&#xff0c;就是可以通过这个机制&#xff0c;让java代码可以调用本地c语言编写的代码&#xff0c;将c语言编写的代码打包成动态库&#xff0c;然后&#…

Java面试重点之反射机制

一、 反射是什么&#xff1f; 允许程序在运行时查询和操作对象的类型信息。通过反射&#xff0c;程序能够在运行时获取对象的类定义信息&#xff0c;如类的名称、方法、字段、注解等&#xff0c;并且可以动态地调用对象的方法或访问其字段&#xff0c;而无需在编译时具体知道对…

CarEye 智能叉车管理系统

CarEye 团队在智能车辆管理平台基础上&#xff0c;专门针对叉车管理特殊性开发了叉车管理系统。以下是叉车管理系统的一些主要介绍&#xff1a;

跟TED演讲学英文:Innovating to zero! by Bill Gates

Innovating to zero! Link: https://www.ted.com/talks/bill_gates_innovating_to_zero Speaker: Bill Gates Date: February 2010 文章目录 Innovating to zero!IntroductionVocabularyTranscriptQ&A with Chris AndersonSummary后记 Introduction At TED2010, Bill Ga…

深度学习突破:LLaMA-MoE模型的高效训练策略

在人工智能领域&#xff0c;大模型&#xff08;LLM&#xff09;的崛起带来了前所未有的进步&#xff0c;但随之而来的是巨大的计算资源需求。为了解决这一问题&#xff0c;Mixture-of-Expert&#xff08;MoE&#xff09;模型架构应运而生&#xff0c;而LLaMA-MoE正是这一架构下…

环形链表题

1.环形链表1 看题&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路1&#xff1a;哈希表 遍历所有节点&#xff0c;每次遍历一个节点时&#xff0c;判断该节点是否被访问过。 可以使用哈希表来存储所有已经访问过的节点。每次到达一个节点&#xff0c;如果该节点已…

windows查看nginx是否启动

windows查看nginx是否启动 1.通过命令提示符: 打开命令提示符&#xff08;CMD&#xff09;。您可以通过按下WinR键&#xff0c;然后输入“cmd”并按下Enter键来打开命令提示符窗口。 输入命令 tasklist /fi “imagename eq nginx.exe”。如果命令执行后能看到nginx进程&#x…

【DeepL】菜鸟教程:如何申请DeepL免费API并使用Python的DeepL

前言 在这篇技术博文中,我们将介绍如何利用DeepL的强大功能,通过其免费API在Python项目中实现高质量的文本翻译。我们将从基础开始,解释DeepL是什么,它的用途,如何申请免费API,以及如何在Python中使用DeepL库。 什么是DeepL? DeepL是一个基于人工智能的翻译服务,它以…

RocketMQ MQTT 快速搭建验证

来自业务的需求&#xff0c;需要快速搭建一套支持 MQTT 协议的消息系统。 前期准备&#xff1a; 官方地址&#xff1a;https://github.com/apache/rocketmq-mqtt RocketMQ从4.9.3 版本开始才支持该功能&#xff0c;所以需要先检查 RocketMQ 的版本是否满足。 RocketMQ 部署参…

Java同时使用@RequestBody和@RequestParam传参在postman中执行请求报错:Unsupported Media Type

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Laravel5.4 反序列化

文章目录 0x01 环境搭建0x02 POP 链0x03 exp0x04 总结 前言&#xff1a;CC 链复现的头晕&#xff0c;还是从简单的 Laravel 开始吧。 laravel 版本&#xff1a;5.4 0x01 环境搭建 laravel安装包下载地址 安装后配置验证页面。在 /routes/web.php 文件中添加一条路由&#xf…

神之浩劫2下载教程 MOBA新游神之浩劫2在哪下载/怎么下载

《神之浩劫2Smite 2》重新定义了MOBA游戏的征服模式&#xff0c;为玩家带来更多的互动和进展。最近的开发者深度挖掘展示了游戏地图的全新设计&#xff0c;既简化了基本操作&#xff0c;又丰富了游戏选择。游戏中的敌人也有了新的进展方式。例如&#xff0c;击败火巨人和金之怒…