暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Objenesis-实例化对象的黑科技

蹲厕所的熊 2018-06-12
1378

蹲厕所的熊 转载请注明原创出处,谢谢!

说起对象实例化,想必没有人不知道吧~,最简单的就是使用new关键字来进行实例化对象的操作,首先我们来定义一个对象Person(是它还是它~)

  1. @Data

  2. public class Person {

  3.    private int age;

  4.    private String name;

  5. }

接下来,我们来把默认的构造函数设置为private,发现不能简单new出一个实例了。

  1. private Person() {}

但是这一步估计也难不住大多数同学,反射不就是用来干这个事的吗?用Person获取到默认的构造函数,设置可访问,最后就可以实例化出Person对象了,so easy~

  1. Constructor<Person> constructor = Person.class.getDeclaredConstructor();

  2. constructor.setAccessible(true);

  3. Person person = constructor.newInstance();

但是反射的前提是得存在构造函数,但是并不是所有的java类都有无参构造函数,并且有的类的构造函数还是private的,更甚有些类是第三方的,我们不能修改源码,这个时候 objenesis
框架就可以发挥它的作用了。我fork出来加了点中文注释,地址:https://github.com/benjaminwhx/objenesis

Objenesis

Objenesis是一个小型java类库用来实例化一个特定class的对象(经过测试有效代码才1538行)。我们先来看看它内部类的继承关系:

Objenesis
内部实现使用策略模式,主要用来提供对象实例化的操作。 ObjenesisBase
是一个基类,内部维护一个由子类提供的策略类 InstantiatorStrategy
,由图中可以看到,策略类主要是 StdInstantiatorStrategy
SerializingInstantiatorStrategy
两个。

  1. public class ObjenesisBase implements Objenesis {


  2.    protected final InstantiatorStrategy strategy;

  3.    protected ConcurrentHashMap<String, ObjectInstantiator<?>> cache;


  4.    public ObjenesisBase(InstantiatorStrategy strategy) {

  5.        this(strategy, true);

  6.    }


  7.    public ObjenesisBase(InstantiatorStrategy strategy, boolean useCache) {

  8.        this.strategy = strategy;

  9.        this.cache = useCache ? new ConcurrentHashMap<String, ObjectInstantiator<?>>() : null;

  10.    }


  11.    public <T> T newInstance(Class<T> clazz) {

  12.        return getInstantiatorOf(clazz).newInstance();

  13.    }


  14.    @SuppressWarnings("unchecked")

  15.    public <T> ObjectInstantiator<T> getInstantiatorOf(Class<T> clazz) {

  16.        // 基本类型抛异常

  17.        if(clazz.isPrimitive()) {

  18.            throw new IllegalArgumentException("Primitive types can't be instantiated in Java");

  19.        }

  20.        if(cache == null) {

  21.            return strategy.newInstantiatorOf(clazz);

  22.        }

  23.        ObjectInstantiator<?> instantiator = cache.get(clazz.getName());

  24.        if(instantiator == null) {

  25.            ObjectInstantiator<?> newInstantiator = strategy.newInstantiatorOf(clazz);

  26.            instantiator = cache.putIfAbsent(clazz.getName(), newInstantiator);

  27.            if(instantiator == null) {

  28.                instantiator = newInstantiator;

  29.            }

  30.        }

  31.        return (ObjectInstantiator<T>) instantiator;

  32.    }

  33. }

它主要就是通过子类提供的策略类来调用 newInstantiatorOf
方法得到实例化器,最后通过实例化器的 newInstance
方法进行实例化操作。

Objenesis
的主要实现有两个,一个是 ObjenesisStd
ObjenesisSerializer

ObjenesisStd
是实例化的标准方式,内部使用 StdInstantiatorStrategy
策略类来提供实例化帮助。

  1. public ObjenesisStd() {

  2.    super(new StdInstantiatorStrategy());

  3. }

ObjenesisSerializer
是实例化的另外一种方式,内部使用 SerializingInstantiatorStrategy
策略类来提供实例化帮助。

  1. public ObjenesisSerializer() {

  2.    super(new SerializingInstantiatorStrategy());

  3. }

InstantiatorStrategy

策略类最终会通过不同的策略来路由不同实现类,我们来看看标准策略类 StdInstantiatorStrategy

  1. public class StdInstantiatorStrategy extends BaseInstantiatorStrategy {


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


  3.        // HOTSPOT 或者 OPENJDK

  4.        if (PlatformDescription.isThisJVM(HOTSPOT) || PlatformDescription.isThisJVM(OPENJDK)) {

  5.            // Java 7 GAE was under a security manager so we use a degraded system

  6.            if (PlatformDescription.isGoogleAppEngine() && PlatformDescription.SPECIFICATION_VERSION.equals("1.7")) {

  7.                if (Serializable.class.isAssignableFrom(type)) {

  8.                    // 返回序列化实例化器

  9.                    return new ObjectInputStreamInstantiator<T>(type);

  10.                }

  11.                // 反射调用无参构造函数来实例化

  12.                return new AccessibleInstantiator<T>(type);

  13.            }

  14.            // UnsafeFactoryInstantiator也可以工作,但是根据测试比Sun的ReflectionFactory慢了2.5倍,所以我更喜欢用它

  15.            return new SunReflectionFactoryInstantiator<T>(type);

  16.        } else if (PlatformDescription.isThisJVM(DALVIK)) {    // 安卓

  17.            if (PlatformDescription.isAndroidOpenJDK()) {

  18.                // Starting at Android N which is based on OpenJDK

  19.                return new UnsafeFactoryInstantiator<T>(type);

  20.            }

  21.            if (ANDROID_VERSION <= 10) {

  22.                // Android 2.3 Gingerbread and lower

  23.                return new Android10Instantiator<T>(type);

  24.            }

  25.            if (ANDROID_VERSION <= 17) {

  26.                // Android 3.0 Honeycomb to 4.2 Jelly Bean

  27.                return new Android17Instantiator<T>(type);

  28.            }

  29.            // Android 4.3 until Android N

  30.            return new Android18Instantiator<T>(type);

  31.        } else if (PlatformDescription.isThisJVM(JROCKIT)) {

  32.            // JROCKIT JVM

  33.            return new SunReflectionFactoryInstantiator<T>(type);

  34.        } else if (PlatformDescription.isThisJVM(GNU)) {

  35.            return new GCJInstantiator<T>(type);

  36.        } else if (PlatformDescription.isThisJVM(PERC)) {

  37.            return new PercInstantiator<T>(type);

  38.        }


  39.        // Fallback instantiator, should work with most modern JVM

  40.        return new UnsafeFactoryInstantiator<T>(type);


  41.    }

  42. }

大多数人应该都是跑在 HotSpot
或者 OpenJDK
上的,所以最终会选择 SunReflectionFactoryInstantiator
类,而针对安卓会选择另外的方式,其他虚拟机也有对应的类来实现。

ObjectInstantiator

Objenisis包含很多平台和jvm的实现,看源码的包结构可以分为:

  • android:Dalvik/Android平台的虚拟机的实例化方式。

  • basic:基本的实例化方式,有前面提到过的反射,还有序列化、Proxy方式。

  • gcj:GCJ编译器的实例化方式。

  • perc:Aonix PERC虚拟机的实例化方式。

  • sun:sun包下的一些黑科技。

这里我们只关心sun的jvm中的Instantiator的黑科技实现。其中的实现大约有三种:

  • SunReflectionFactoryInstantiator:使用 sun.reflect.ReflectionFactory
     来创建对象。

  • SunReflectionFactorySerializationInstantiator:也是使用 sun.reflect.ReflectionFactory
     来创建对象,但是为了兼容序列化,需要找到第一个没有实现serializable接口的父类,性能有一定的损耗。

  • UnsafeFactoryInstantiator:使用 sun.misc.Unsafe.allocateInstance
     来创建对象。

还有一种MagicInstantiator看上去没什么用。看作者的测试更推荐使用 SunReflectionFactoryInstantiator
类来实例化对象。

SunReflectionFactoryInstantiator

  1. @Instantiator(Typology.STANDARD)

  2. public class SunReflectionFactoryInstantiator<T> implements ObjectInstantiator<T> {


  3.    private final Constructor<T> mungedConstructor;


  4.    public SunReflectionFactoryInstantiator(Class<T> type) {

  5.        Constructor<Object> javaLangObjectConstructor = getJavaLangObjectConstructor();

  6.        // 这个函数是关键,主要是为这个class创建了一个新的constractor

  7.        mungedConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(

  8.                type, javaLangObjectConstructor);

  9.        mungedConstructor.setAccessible(true);

  10.    }


  11.    // 创建对象

  12.    public T newInstance() {

  13.        try {

  14.            return mungedConstructor.newInstance((Object[]) null);

  15.        } catch (Exception e) {

  16.            throw new ObjenesisException(e);

  17.        }

  18.    }


  19.    // //获得object的对象的构造函数

  20.    private static Constructor<Object> getJavaLangObjectConstructor() {

  21.        try {

  22.            return Object.class.getConstructor((Class[]) null);

  23.        } catch (NoSuchMethodException e) {

  24.            throw new ObjenesisException(e);

  25.        }

  26.    }

  27. }

最关键的还是 newConstructorForSerialization
这个方法(代码太长,缩减了一点代码)

  1. class SunReflectionFactoryHelper {


  2.    public static <T> Constructor<T> newConstructorForSerialization(Class<T> type,

  3.                                                                    Constructor<?> constructor) {

  4.        Class<?> reflectionFactoryClass = getReflectionFactoryClass();

  5.        // 实例化ReflectionFactory类

  6.        Object reflectionFactory = createReflectionFactory(reflectionFactoryClass);


  7.        // 获取ReflectionFactory的newConstructorForSerialization方法

  8.        Method newConstructorForSerializationMethod = getNewConstructorForSerializationMethod(

  9.                reflectionFactoryClass);


  10.        // 调用newConstructorForSerialization获得一个被改写的构造函数

  11.        return (Constructor<T>) newConstructorForSerializationMethod.invoke(

  12.                reflectionFactory, type, constructor);

  13.    }


  14.    private static Class<?> getReflectionFactoryClass() {

  15.        return Class.forName("sun.reflect.ReflectionFactory");

  16.    }


  17.    private static Object createReflectionFactory(Class<?> reflectionFactoryClass) {

  18.        Method method = reflectionFactoryClass.getDeclaredMethod(

  19.                    "getReflectionFactory");

  20.    }


  21.    private static Method getNewConstructorForSerializationMethod(Class<?> reflectionFactoryClass) {

  22.        return reflectionFactoryClass.getDeclaredMethod(

  23.                    "newConstructorForSerialization", Class.class, Constructor.class);

  24.    }

  25. }

注释写的很清楚,关键就在于 sun.reflect.ReflectionFactory
newConstructorForSerialization
方法,这个方法返回的是一个无参的constructor对象,但是绝对不会与原来的constructor冲突,被称为munged 构造函数。

仿照它尝试实例化之前Person类(去掉默认的构造函数)

  1. @Data

  2. public class Person {

  3.    private int age;

  4.    private String name;


  5.    public Person(int age, String name) {

  6.        this.age = age;

  7.        this.name = name;

  8.    }

  9. }


  10. public static void main(String[] args) throws Exception {

  11.    ObjectInstantiator<Person> objectInstantiator = new SunReflectionFactoryInstantiator<>(Person.class);

  12.        Person person = objectInstantiator.newInstance();

  13. }

反射操作比较耗时,这里可以提个代码给作者优化一下~

SunReflectionFactorySerializationInstantiator

  1. public class SunReflectionFactorySerializationInstantiator<T> implements ObjectInstantiator<T> {


  2.    private final Constructor<T> mungedConstructor;


  3.    public SunReflectionFactorySerializationInstantiator(Class<T> type) {

  4.        // 返回指定类的第一个非序列化的父类(包括自己)

  5.        Class<? super T> nonSerializableAncestor = SerializationInstantiatorHelper

  6.                .getNonSerializableSuperClass(type);


  7.        Constructor<? super T> nonSerializableAncestorConstructor;

  8.        try {

  9.            // 获得无参构造函数(该类必须有无参构造函数)

  10.            nonSerializableAncestorConstructor = nonSerializableAncestor

  11.                    .getDeclaredConstructor((Class[]) null);

  12.        }

  13.        catch(NoSuchMethodException e) {

  14.            throw new ObjenesisException(new NotSerializableException(type+" has no suitable superclass constructor"));

  15.        }


  16.        //获得munged构造函数对象,这个构造函数中会调用nonSerializableAncestorConstructor

  17.        mungedConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(

  18.                type, nonSerializableAncestorConstructor);

  19.        mungedConstructor.setAccessible(true);

  20.    }


  21.    public T newInstance() {

  22.        try {

  23.            return mungedConstructor.newInstance((Object[]) null);

  24.        }

  25.        catch(Exception e) {

  26.            throw new ObjenesisException(e);

  27.        }

  28.    }

  29. }

它从指定类往父类一直找,返回第一个没有实现序列化的类,但是这个类必须要有无参的构造函数,接着还是和和刚才一样,调用 sun.reflect.ReflectionFactory
newConstructorForSerialization
方法。

为了印证猜想,让Person类实现了序列化,并继承了一个类 Animal

  1. @Data

  2. public class Person extends Animal implements Serializable {

  3.    private int age;

  4.    private String name;


  5.    public Person(int age, String name) {

  6.        this.age = age;

  7.        this.name = name;

  8.    }

  9. }


  10. @Data

  11. public class Animal {

  12.    String animalName;


  13.    public Animal() {

  14.        System.out.println("print animal constructor");

  15.    }

  16. }

把实例化器换一下,输出结果果然打印了 printanimal constructor

UnsafeFactoryInstantiator

  1. public class UnsafeFactoryInstantiator<T> implements ObjectInstantiator<T> {


  2.    private final Unsafe unsafe;

  3.    private final Class<T> type;


  4.    public UnsafeFactoryInstantiator(Class<T> type) {

  5.        this.unsafe = UnsafeUtils.getUnsafe();

  6.        this.type = type;

  7.    }


  8.    public T newInstance() {

  9.        try {

  10.            // 使用unsafe的allocateInstance实例化对象

  11.            return type.cast(unsafe.allocateInstance(type));

  12.        } catch (InstantiationException e) {

  13.            throw new ObjenesisException(e);

  14.        }

  15.    }

  16. }

代码很简单,使用了 Unsafe
类的 allocateInstance
方法。



如果读完觉得有收获的话,欢迎点赞、关注、加公众号【蹲厕所的熊】,查阅更多精彩历史!!!

文章转载自蹲厕所的熊,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论