最近打算将知识点学的细致一点,之前分析过java原生的序列化和反序列化流程,还算是比较的细;但是今天想了一个问题,在反序列化的时候如果目标类继承Serializable,那么在反序列化的时候不会触发其构造函数;当然我们知道,在java中实例化对象的方式一般有五种;
今天着重探究一下反序列化创建对象的方式;
1 | import java.io.*; |
简单来看一个demo;
之前文章讲述过,ObjectStreamClass函数对一个class进行内省并且封装,里面有一个敏感的函数用来获取constructor;
追踪进去这个函数内部看一下逻辑;
内部实现了reflFactory.newConstructorForSerialization;下面就对其进行一个分析;
其实这里就是网上广泛说的,先拿到非继承Serializable类的构造函数然后再基于此生成新的construcotr;可以看一下上面图中传入的参数就可,cl和cons;
跟进去看一下内部的实现逻辑;
可以看到首先进行了constructor判断,判断当前的构造器是否为需要实例化对象的构造器,这里显然不是,这也是由反序列化中的逻辑决定的,会先进行“上”溯;拿到非继承Serializable类的构造函数;这里直接去到基类里了;判断不是之后就会进入到generateConstructor的逻辑;很显然就要创造一个新的构造器;这一步主要是因为:Constructor 对象中有一个关键属性 private volatile ConstructorAccessor constructorAccessor; ,ConstructorAccessor 是一个接口 如果构造方法不存在是没有办法反射生成的,reflectionFactory.newConstructorForSerialization 使用字节码的方式生成了这个接口的实现类。下面就来简单理解一下:
看一下实现的源码,先去实例化一个MethodAccessorGenerator之后调用generateSerializationConstructor函数:
看一下逻辑不难理解;传入目标的参数,从而去创建一个constructor;上面已经说过相关的原理了,采用字节码的方式进行生成;
具体的实现逻辑在generate函数中实现;中间的一些实现逻辑简单跳过;最后生成点是在下面:
这里的byte就是最后的字节码,其相关的过程就是上面省略的那一部分;不过可以向上简单看一下最后的转化点:
最开始的传入点:
期间add或者set一些东西:
最后导出字节码,然后defineclass进行加载的操作;
这里看一下相关的逻辑,有一个需要去注意的点,在defineclass的时候,每次加载的时候有可能是不同的Classloader declaringClass.getClassLoader()).newInstance();所以如果一直大量的使用,不对Constructor 对象进行缓存,会不停的加载类 最终导致 Metaspace 空间不足,可能会频繁的触发 FullGC 的情况。
最后进行defineclass之后生成Class对象,Name为
最后返回ConstructorAccessor接口的实现类;
然后通过setConstructorAccessor进行set;然后返回;
到此相比就很明白为什么之后尽管调用Object的newinstance也会实例化information的实例了吧;
回看一下Constructor这个class下的newInstance方法;
原理已经很透彻了;感兴趣的师傅可以进一步分析一下Constructor这个class下的实现逻辑,就会更加的明白相关的原理;