在Windows和Linux上,Java对有关继承方法的反思是否有所不同?

时间:2009-03-09 09:18:04

标签: java windows linux reflection

在设置Hudson进行持续集成测试(在JeOS服务器上)时,我遇到了一些奇怪的行为,我希望SO的优秀人员可以向我解释。

我们的单元测试在很大程度上依赖于域对象的使用,必须设置许多属性(由于数据库中的空约束)。为了使我们的测试可读,我们创建了一个类InstantiationUtils,它可以实例化一个对象并通过反射设置一系列属性:

public static <T> T newInstance(final Class<T> type, final KeyValuePair<?>... propertyValues) {

    return ReflectionUtils.reflectionOperation(new ReflectionOperation<T>() {

        @Override
        public T perform() throws Exception {

            T object = type.newInstance();
            for (KeyValuePair<?> propertyValue : propertyValues) {

                String propertyName = propertyValue.getKey();
                Object value = propertyValue.getValue();
                String setterName = "set" + StringUtils.capitalize(propertyName);
                ReflectionUtils.invoke(object, setterName, value);
            }
            return object;
        }
    });
}

public static void invoke(final Object target, final String methodName, final Object... params) {

    List<Class<?>> parameterTypes = ListUtils.map(asList(params), "class");
    Class<?> targetClass = target.getClass();
    Method method = MethodUtils.getMatchingAccessibleMethod(targetClass, methodName,
        parameterTypes.toArray(new Class<?>[] {}));
    invoke(target, method, params);
}

public class Foo {
    private String foo;

    public void setFoo(final String foo) {
        this.foo = foo;
    }
}

public class Bar extends Foo {
    private String bar;

    public void setBar(final String bar) {
       this.bar = bar;
    }
}

不幸的是,编写此代码的人不再适合我们,但据我所知,它没有任何问题。对于Windows也是如此 - 我们在整个单元测试中使用InstantiationUtils而没有任何问题。

然而,Linux是不同的。事实证明,在Linux中,newInstance()方法仅适用于我们想要实例化的类的直接(即未继承)成员。

InstantiationUtils.newInstance(Bar.class,“bar”,“12345”);将工作,而InstantiationUtils.newInstance(Bar.class,“foo”,“98765”);将在Linux上失败,但有以下例外:

  

xxx.xxx.xxx.ReflectionUtils $ ReflectionException:java.lang.NoSuchMethodException:属性'foo'没有setter方法

在Windows上,两个调用都可以工作(我知道newInstance签名不匹配;我们有几个重载的newInstance()方法将参数转换为KeyValuePairs)。

我很难接受继承的公共方法的处理方式不同,所以我已经用我能想到的各种方式对其进行了测试。并且它总是得出结论,在Linux下,至少使用Reflection的上述用法,我们无法访问公共继承的方法。

在Windows上,我使用的是Sun的JRE 1.6.0.11,在Linux中它也是Sun,但是版本是1.6.0.7。

任何人都可以确认这是否正确?或者反射用法是否有些缺陷?

7 个答案:

答案 0 :(得分:3)

您正在使用MethodUtils,它有一些limitations

  

已知限制

     

在默认访问超类中访问公共方法

     

调用默认访问超类中包含的公共方法时会出现问题。 Reflection可以很好地定位这些方法并正确地将它们指定为公共方法。但是,如果调用该方法,则抛出IllegalAccessException。

要检查的另一件事是setFoo()方法是否重载,这也可能导致问题......

答案 1 :(得分:2)

可能是SecurityManager设置在不同的Java运行时之间有所不同吗?

当然我怀疑这是平台问题 - 这几乎肯定与两个环境之间的JRE版本/设置有关

您确实需要将源代码发布到 MethodUtils.getMatchingAccessibleMethod

答案 2 :(得分:1)

要尝试的几件事......

在Linux上,尝试在不对getFoo()进行反射调用的情况下编写代码 - 如果它不能编译,那么反射就没有希望工作了(这取决于yoiu如何在运行时设置CLASSAPTH ...)

尝试添加以下代码并在Linux和Windows上运行。

final Properties properties;

properties = System.getProperties();

for(final Entry<Object, Object> entry : properties.entrySet())
{
    System.out.println(entry.getKey() + " " + entry.getValue());
}

检查输出以确保您使用的是smae JDK / JRE。还要检查以确保类路径正确,以便您实际加载您认为正在加载的内容。

答案 3 :(得分:1)

神秘部分解决了:

MethodUtils.getMatchingAccessibleMethod()显然在Linux和Windows上的工作方式不同。

通过使用MethodUtils.getAccessibleMethod(),它可以工作。为什么,我不知道,但我猜测MethodUtils在弄清楚Method应该有什么签名时会以某种方式误解参数列表。

我想花更多的时间来研究这个问题,但是一如既往要做的事情和要交付的项目,所以我必须接受getAccessibleMethod的工作,然后继续: - )

感谢大家的投入!

答案 4 :(得分:0)

您在Linux,Sun,GCJ等上使用哪种JVM?如果你使用的不是Sun的JVM,你可以尝试安装它,看看是否有所作为。

答案 5 :(得分:0)

您有不同的区域设置吗? StringUtils.capitalize(propertyName)可能产生不同的输出。

答案 6 :(得分:0)

您检查了CLASSPATH吗?您是否正在选择要实例化的类的不同版本,具体取决于您所在的平台? (例如旧的代码库等等?)