2

标题可能看起来令人不安,但让我解释一下。

我面临着一个有趣的挑战,我有一个层次结构的类,这些层次结构与一个对象相关联,该对象存储与其每个属性相关的元数据(一个带有诸如 UPDATED 或 NO_UPDATE 之类的编辑标志的 int 值枚举)。

合并两个对象时出现问题,因为我不想检查类上的每个字段以查看它是否已更新并跳过或应用更改。

我的想法:反思。

所有对象都在接口后面,因此我可以使用 IObject.class.getMethods() 并以这种方式迭代该数组:

IClass class = //Instance of the first class;
IAnotherClass anotherClass = //Instance of the second class;
for(Method m  : IObject.class.getMethods()) {
    if(m.getName().startsWith("get")) {
        try {
            //Under this method (which is a getter) I cast it on 
            //both classes who implement interfaces that extend an
            //interface that defines the getters to make them
            //consistent and ensure I'll invoke the same methods.
            String propertyClass = (String)m.invoke(class);
            String propertyAnotherClass = (String)m.invoke(anotherClass);
            if(propertyClass != propertyAnotherClass) {
                //Update attribute and attribute status.
            }
        } catch (Exception e) {

        }
    }
}

是否有另一种方法来实现这一点,或者我应该坚持使用冗长的方法来调用每个属性的属性并进行这样的检查?对象不会发生太大变化,而且架构非常模块化,因此如果字段发生变化,则不会涉及太多更新,但必须更改这样的方法让我有点担心。

编辑1:我正在发布到目前为止我所拥有的工作代码。这段代码对我来说是一个解决方案,但它很有效,我将它用作最后的资源不是因为我有时间花,而是因为我不想重新发现轮子。如果我使用它,我将使用方法制作一个静态列表,因此我只需要获取该列表一次,考虑到 AlexR 指出的事实。

private static void merge(IClazz from, IClazz to) {
        Method methods[] = from.getClass().getDeclaredMethods();
        for(Method m : methods) {
            if(m.getName().startsWith("get") && !m.getName().equals("getMetadata")) {
                try {                   
                    String commonMethodAnchor = m.getName().split("get")[1];
                    if(!m.getReturnType().cast(m.invoke(from)).equals(m.getReturnType().cast(m.invoke(to)))) {
                        String setterMethodName = "set" + commonMethodAnchor;
                        Method setter = IClazz.class.getDeclaredMethod(setterMethodName, m.getReturnType());
                        setter.invoke(to, m.getReturnType().cast(m.invoke(from)));
                        //Updating metadata
                        String metadataMethodName = "set" + commonMethodAnchor + "Status";
                        Method metadataUpdater = IClazzMetadata.class.getDeclaredMethod(metadataMethodName, int.class);
                        metadataUpdater.invoke(to.getMetadata(), 1);
                    }

                } catch (Exception e) {

                }
            }
        }
    }

metadataUpdater 将值设置为 1 只是为了模拟我在真实案例场景中使用的“更新”标志。

编辑 3:感谢 Juan、David 和 AlexR 的建议和指导!他们真的让我考虑我一开始没有考虑的事情(我赞成你所有的答案,因为他们都帮助了我)。

在添加了 AlexR 建议的内容并检查了 jDTO 和 Apache Commons(最终发现总体概念非常相似)之后,我决定坚持我的代码而不是使用其他工具,因为它在给定对象层次结构和元数据的情况下工作解决方案的结构,到目前为止没有出现异常。该代码是第二次编辑的代码,我将它放在一个帮助类中,最终完成了这个技巧。

4

3 回答 3

3

Apache Commons Bean Utils 可能会解决您的问题:http ://commons.apache.org/beanutils/

如果要复制所有属性,请尝试使用 copyProperties:http ://commons.apache.org/beanutils/v1.8.3/apidocs/src-html/org/apache/commons/beanutils/BeanUtils.html#line.134

看一个例子:http ://www.avajava.com/tutorials/lessons/how-do-i-copy-properties-from-one-bean-to-another.html

    FromBean fromBean = new FromBean("fromBean", "fromBeanAProp", "fromBeanBProp");
    ToBean toBean = new ToBean("toBean", "toBeanBProp", "toBeanCProp");
    System.out.println(ToStringBuilder.reflectionToString(fromBean));
    System.out.println(ToStringBuilder.reflectionToString(toBean));
    try {
        System.out.println("Copying properties from fromBean to toBean");
        BeanUtils.copyProperties(toBean, fromBean);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    System.out.println(ToStringBuilder.reflectionToString(fromBean));
    System.out.println(ToStringBuilder.reflectionToString(toBean));
于 2012-04-24T18:52:11.303 回答
2

我认为最好的方法是使用代理对象,无论是动态代理还是 cglib 增强器或类似的东西,所以你装饰了 getter 和 setter,你可以跟踪那里的变化。

希望能帮助到你。

于 2012-04-24T18:55:06.553 回答
1

您的方法没问题,但请记住,这getMethod()比 慢得多invoke(),因此如果您的代码对性能至关重要,您可能需要缓存Method对象。

于 2012-04-24T18:55:33.023 回答