简体   繁体   English

Java 对继承方法的反射在 Windows 和 Linux 中是否不同?

[英]Is Java Reflection on Inherited Methods Different in Windows and Linux?

While setting up Hudson for continous integration testing (on a JeOS server), I've come across some strange behaviour I'm hoping the fine people at SO can explain to me.在设置 Hudson 以进行持续集成测试(在 JeOS 服务器上)时,我遇到了一些奇怪的行为,我希望 SO 的优秀人员可以向我解释。

Our unit tests depend heavily on the use of domain objects, with lots of properties that must be set (due to null constraints in the database).我们的单元测试很大程度上依赖于域对象的使用,必须设置许多属性(由于数据库中的 null 约束)。 In order to keep our tests readable, we have created a class InstantiationUtils that can instantiate an object and set a series of properties through reflection:为了让我们的测试保持可读性,我们创建了一个 class InstantiationUtils 可以实例化一个 object 并通过反射设置一系列属性:

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;
    }
}

The person who wrote this code unfortunately no longer works for us, but as far as I can see, there is nothing wrong with it.不幸的是,编写此代码的人不再为我们工作,但据我所知,它没有任何问题。 Which is also true for Windows - we use InstantiationUtils throughout our unit tests without any problems. Windows 也是如此——我们在整个单元测试中使用 InstantiationUtils 没有任何问题。

Linux, however, is different.然而,Linux 不同。 It turns out that in Linux, the newInstance() method only works for direct (ie not inherited) members of the class we want to instantiate.事实证明,在 Linux 中,newInstance() 方法仅适用于我们要实例化的 class 的直接(即非继承)成员。

InstantiationUtils.newInstance(Bar.class, "bar", "12345"); InstantiationUtils.newInstance(Bar.class, "bar", "12345"); will work, while InstantiationUtils.newInstance(Bar.class, "foo", "98765");将工作,而 InstantiationUtils.newInstance(Bar.class, "foo", "98765"); will fail on Linux, with the following exception:将在 Linux 上失败,但以下异常:

xxx.xxx.xxx.ReflectionUtils$ReflectionException: java.lang.NoSuchMethodException: Property 'foo' has no setter method xxx.xxx.xxx.ReflectionUtils$ReflectionException: java.lang.NoSuchMethodException: 属性 'foo' 没有设置方法

On Windows, both calls will work (I know the newInstance signature doesn't match; we have several overloaded newInstance() methods that convert the parameters to KeyValuePairs).在 Windows 上,两个调用都可以工作(我知道 newInstance 签名不匹配;我们有几个重载的 newInstance() 方法可以将参数转换为 KeyValuePairs)。

I had a hard time accepting that inherited public methods are treated differently, so I have tested this in all ways I can think of.我很难接受继承的公共方法被区别对待,所以我用我能想到的所有方式对此进行了测试。 And it always ends up with the conclusion that under Linux, at least with the above usage of Reflection, we can't access public inherited methods.并且总是得出结论,在Linux下,至少在上面使用反射的情况下,我们无法访问公共继承方法。

On Windows, I use Sun's JRE 1.6.0.11, in Linux it's also Sun, but version 1.6.0.7.在 Windows 上,我使用 Sun 的 JRE 1.6.0.11,在 Linux 中它也是 Sun,但版本 1.6.0.7。

Can anyone confirm if this is correct?谁能确认这是否正确? Or is the Reflection usage somehow flawed?或者反射的使用是否存在某种缺陷?

You are using MethodUtils, and it has some limitations :您正在使用 MethodUtils,它有一些限制

Known Limitations已知限制

Accessing Public Methods In A Default Access Superclass在默认访问超类中访问公共方法

There is an issue when invoking public methods contained in a default access superclass.调用默认访问超类中包含的公共方法时存在问题。 Reflection locates these methods fine and correctly assigns them as public.反射可以很好地定位这些方法并正确地将它们分配为公共的。 However, an IllegalAccessException is thrown if the method is invoked.但是,如果调用该方法,则会引发 IllegalAccessException。

Another thing to check is if the setFoo() method is overloaded, this may also cause the problem...要检查的另一件事是 setFoo() 方法是否重载,这也可能导致问题...

Could it be that the SecurityManager settings are different between the different Java runtimes?会不会是不同的 Java 运行时之间的SecurityManager设置不同?

Certainly I doubt that this is a platform issue - it is almost certainly something to do with the JRE version/setup between the two environments当然,我怀疑这是一个平台问题——几乎可以肯定这与两个环境之间的 JRE 版本/设置有关

You really need to post the source code to MethodUtils.getMatchingAccessibleMethod您确实需要将源代码发布到MethodUtils.getMatchingAccessibleMethod

A couple of things to try...有几件事要尝试...

On Linux, try comping the code without a reflective call to getFoo() - if it will not compile then reflection has no hope of working (well it does depending on how yoiu setup the CLASSAPTH at runtime...)在 Linux 上,尝试在没有反射调用 getFoo() 的情况下编译代码 - 如果它不能编译,那么反射就没有工作的希望(这取决于你在运行时如何设置 CLASSAPTH ......)

Try adding the code below and run it on both Linux and Windows.尝试添加下面的代码并在 Linux 和 Windows 上运行它。

final Properties properties;

properties = System.getProperties();

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

The check the output to make sure that you are using the smae JDK/JRE.检查 output 以确保您使用的是 smae JDK/JRE。 Also check to make sure that the classpath is correct so that you are actually loading what you think you are loading.还要检查以确保类路径正确,以便您实际加载您认为正在加载的内容。

Mystery partially solved:谜团部分解决:

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

By using MethodUtils.getAccessibleMethod() instead, it works.通过使用 MethodUtils.getAccessibleMethod() 代替,它可以工作。 Why, I don't know, but I'm guessing that MethodUtils somehow misinterprets the parameter list when figuring out what signature the Method should have.为什么,我不知道,但我猜测 MethodUtils 在确定 Method 应该具有的签名时会以某种方式误解参数列表。

I'd like to spend more time investigating this, but as always there are things to do and projects to deliver, so I just have to accept that getAccessibleMethod works, and move on:-)我想花更多时间对此进行调查,但与往常一样,有一些事情要做,有一些项目要交付,所以我只需要接受 getAccessibleMethod 有效,然后继续:-)

Thanks to everyone for their input!感谢大家的意见!

Which JVM do you use on Linux, Sun, GCJ etc?您在 Linux、Sun、GCJ 等上使用哪个 JVM? If you use something else than Sun's JVM, you could try installing it and see if that makes a difference.如果您使用的不是 Sun 的 JVM,您可以尝试安装它,看看是否有区别。

Do you have different locales?你有不同的地区吗? StringUtils.capitalize(propertyName) may be producing different output. StringUtils.capitalize(propertyName)可能会产生不同的 output。

Have you checked your CLASSPATH ?你检查过你的CLASSPATH吗? Are you picking up different versions of the class you want to instantiate depending on which platform you're on?您是否根据您所在的平台选择不同版本的 class 要实例化? (eg old codebases lying around etc.?) (例如旧代码库等?)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM