对Windows和Linux中的inheritance方法有不同的Java反思?

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

我们的unit testing在很大程度上依赖于域对象的使用,因为必须设置很多属性(由于数据库中存在空限制)。 为了保持我们的testing可读性,我们创build了一个InstantiationUtils类,它可以实例化一个对象并通过reflection设置一系列属性:

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也是如此 – 我们在整个unit testing中使用InstantiationUtils没有任何问题。

但是Linux不同。 事实certificate,在Linux中,newInstance()方法只适用于我们要实例化的类的直接成员(即不是inheritance的成员)。

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)。

我很难接受inheritance的公共方法被区别对待,所以我已经以我能想到的所有方式进行了testing。 而且总是会得出这样的结论:在Linux下,至less使用Reflection的上述用法,我们不能访问公有的inheritance方法。

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

任何人都可以确认这是否正确? 或者是reflection的用法有瑕疵?

您正在使用MethodUtils,它有一些限制 :

已知的限制

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

调用默认访问超类中包含的公共方法时存在问题。 反射查找这些方法罚款,并正确地将其分配为公共。 但是,如果方法被调用,则会引发IllegalAccessException。

另一件要检查的是,如果setFoo()方法被重载,这也可能会导致问题…

是不是在不同的Java运行时间的SecurityManager设置是不同的?

当然,我怀疑这是一个平台问题 – 几乎可以肯定这两个环境之间的JRE版本/设置

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

一些事情要尝试…

在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。 同时检查以确保类路径是正确的,以便实际上加载您认为正在加载的内容。

神秘的部分解决:

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

通过使用MethodUtils.getAccessibleMethod()来代替它。 为什么,我不知道,但我猜测,MethodUtils在弄清楚方法应该具有什么签名时会错误地解释参数列表。

我想花更多的时间来研究这一点,但一如既往有事情要做,项目交付,所以我只需要接受getAccessibleMethod的工作,并继续前进:-)

感谢大家的意见!

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

你有不同的地区? StringUtils.capitalize(propertyName)可能会产生不同的输出。

你有没有检查你的CLASSPATH ? 你是否正在拾取你想实例化的类的不同版本,取决于你所在的平台? (例如,旧的代码库在哪里?)