概念验证:如何使用Java中的反射动态选择可用的构造函数

时间:2011-06-15 13:10:14

标签: java serialization reflection deserialization

我正在开发一个概念验证对象(de)序列化框架,理想情况下能够序列化任何Object并收集有关类本身的信息。我开始使用Reflection实现它:

  • 访问类型层次结构(超类,接口等)
  • 查找该对象上的所有字段,并获取该字段中的所有值

序列化是'简单'部分,可以实现将此规则递归地应用于对象,直到找到null或基本类型。现在,这就是我被困住的地方:反序列化。

从一个简单的对象开始,“Hello World”字符串,我有这个序列化:

<object type="java.lang.String">
    <primitive name="count" type="int" value="11 />
    <primitive name="hash" type="int" value="0" />
    <primitive name="offset" type="int" value="0" />
    <array name="value" basetype="char">
        <value>H</value>
        <value>e</value>
        <value>l</value>
        ...
        <value>r</value>
        <value>l</value>
        <value>d</value>
    </array>
</object>

反序列化是可以的,因为String类有一个默认构造函数,我可以通过Reflection调用它,我可以设置所有字段。现在,让我们假设我有一个对象的以下序列化:

<object class="some-class-with-no-default-constructor">
    <object name="some-attrib-name" class="attrib-1-class">
    <primitive name="size" type="int" value="5" />
...
</object>

如果我没有默认构造函数会发生什么?接收参数的所有其他构造函数都不能接受'null'值作为输入,引发某种异常,因此我无法通过反射实例化该类?

问题是:“有没有办法实例化某个类的'空对象',以便在实例化后手动设置其字段而不调用其构造函数?”。当然,我也愿意讨论其他策略。

谢谢。


编辑

一旦它是一个概念验证环境,因此我没有考虑安全限制,我找到了一种方法来实例化任何对象而不通过Unsafe类调用它的构造函数。

public final class A {
    private final Object o;
    private A(final Object o) { if (o == null) throw new Error(); this.o = o; }
    public static A a() { return new A(new Object()); }
    public Object getO() { return o; }
}

上面显示的这个类是在下面的一个答案中提出的,它可以被实例化并且它的最终值设置正确(当然,前提是安全限制不适用),使用以下代码:

private static Unsafe getUnsafe() throws Exception {
    Field vDeclaredField = Unsafe.class.getDeclaredFields()[0];
    vDeclaredField.setAccessible(true);
    Unsafe vUnsafe = (Unsafe) vDeclaredField.get(null);
    vDeclaredField.setAccessible(false);
    return vUnsafe;
}

public static void main(String[] args) throws Exception {
    A objectA = (A) getUnsafe().allocateInstance(A.class);

    Field fieldO = A.class.getDeclaredField("o");
    boolean oldAccessibilityValue = fieldO.isAccessible();
    fieldO.setAccessible(true);
    Object objectOParameter = Arrays.asList(1,2,3,4); //could be any object
    fieldO.set(objectA, objectOParameter);
    fieldO.setAccessible(oldAccessibilityValue); //I personally prefer setting it to old value

    assert(objectOParameter.equals(objectA.getO()));
}

所以?你们能看到与SecurityManager本身无关的任何其他问题吗?

3 个答案:

答案 0 :(得分:4)

答案 1 :(得分:0)

  

有没有办法实例化   要设置的某个类的“空对象”   他们的字段后手动   实例化而不调用它   构造

不,没有。这就是为什么基于反射的框架或库通常要求它们使用的类遵循JavaBeans规范,这需要一个默认的构造函数。

克服这种情况的一种方法是要求某些类型的元数据(注释,xml)用于没有默认构造函数的类,它会告诉您使用什么值调用哪个构造函数。

答案 2 :(得分:0)

使用反射,没有可靠的方法,尽管你可以根据声明的构造函数的参数类型进行有根据的猜测。

您可以查看Objenesis

您也可以查看manipulating the byte code at run-time