开源库—使用Objenesis实例化对象

在Java jdk可以通过java.lang.Class的实例调用newInstance()动态实例化Java类。但并不是所有的java类都有无参构造函数,并且有的类的构造函数是private的,有些类是第三方的,我们不能修改源码。Objenesis库通过绕开对象实例构造器克服了这个限制,可以不需要无参构造器而实例化java类。

Objenesis的一般用处有:

  • 序列化、远程处理和持久化:无需调用代码即可将Java类实例化并存储特定状态。
  • 代理、AOP库和Mock对象:可以创建特定Java类的子类而无需考虑super()构造器。
  • 容器框架:可以用非标准方式动态实例化Java类。例如Spring引入Objenesis后,Bean不再必须提供无参构造器了。Spring-Core的源码中即用到了Objenesis库。

快速入门

Objenesis有两个主要的接口:

ObjectInstantiator - 实例化一个类的多个实例。

1
2
3
4
5
6
7
8
9
10
11
public interface ObjectInstantiator<T> {

/**
* Returns a new instance of an object. The returned object's class is defined by the
* implementation.
*
* @return A new instance of an object.
*/
T newInstance();

}

InstantiatorStrategy - 是对于如何实例化一个类的特殊处理策略(不同的Class是不一样的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Defines a strategy to determine the best instantiator for a class.
*/
public interface InstantiatorStrategy {

/**
* Create a dedicated instantiator for the given class
*
* @param <T> Type to instantiate
* @param type Class that will be instantiated
* @return Dedicated instantiator
*/
<T> ObjectInstantiator<T> newInstantiatorOf(Class<T> type);
}

根据jvm供应商,版本和类的安全管理和类型的不同, Objenesis实例化对象有许多不同的策略。
有两种不同种类的实例化是必需的:

  • Stardard - 没有构造器会被调用
  • Serilizable compliant - 其行为有点类似于通过java标准序列化方式来实例化一个对象,这就要求被实例化的类实现了Serializable接口,否则会报异常:NotSerializableException。其实例化的方式是循环去找第一个没有实现Serializable接口的父类的class对象,调用这个父类的构造器。

最简单的使用Objenesis的方法是使用ObjenesisStd(Standard) 和ObjenesisSerializer(Serializable compliant),默认都会自动选择最优策略,不需要使用者考虑应该使用什么策略。这两个类都继承自ObjenesisBase,ObjenesisBase实现了Objenesis。

代码示例

1
2
3
4
Objenesis objenesis = new ObjenesisStd(true); //或者:Objenesis objenesis = new ObjenesisSerializer(true);

//实例化
MyThingy instance = objenesis.newInstance(MyThingy.class);

如果要为一个类创建多个对象,可以使用ObjectInstantiator

1
2
3
4
5
6
Objenesis objenesis = new ObjenesisStd(true); //或者:Objenesis objenesis = new ObjenesisSerializer(true);
ObjectInstantiator thingyInstantiator = objenesis.getInstantiatorOf(MyThingy.class);

MyThingy thingy2 = (MyThingy)thingyInstantiator.newInstance();
MyThingy thingy3 = (MyThingy)thingyInstantiator.newInstance();
MyThingy thingy4 = (MyThingy)thingyInstantiator.newInstance();

性能和线程

为了提高性能,最好是尽可能重用ObjectInstantiator对象。 例如,如果要为一个类示例化多个对象,可以使用同一个ObjectInstantiator实例。
InstantiatorStrategy和ObjectInstantiator都可以在多线程中共享并发使用,它们是线程安全的。

------ 本文完 ------