在Java jdk可以通过java.lang.ClassnewInstance()
动态实例化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
11public 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 | Objenesis objenesis = new ObjenesisStd(true); //或者:Objenesis objenesis = new ObjenesisSerializer(true); |
如果要为一个类创建多个对象,可以使用ObjectInstantiator
。1
2
3
4
5
6Objenesis 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都可以在多线程中共享并发使用,它们是线程安全的。