【问题标题】:JavaBeans alternatives?JavaBeans 的替代品?
【发布时间】:2009-01-13 23:05:05
【问题描述】:

我讨厌 JavaBeans 模式,我的热情就像一千个太阳的火焰一样燃烧。为什么?

  • 详细。现在是 2009 年。我不应该为房产写 7 LOC。如果他们有事件侦听器,那么请抓住你的帽子。
  • 没有类型安全的引用。没有类型安全的方式来引用属性。 Java 的全部意义在于它是类型安全的,它最流行的模式根本不是类型安全的。

我想要的是这样的:

class Customer {
    public Property<String> name = new Property();
}

我主要是一名网络开发人员,因此需要 JPA 和 Wicket 支持。

帮我下javabean火车!

【问题讨论】:

  • “他们鼓励字符串引用”是什么意思?
  • 我认为他的意思是很多网络框架和其他东西最终需要通过反射来引用它们。不过,我不确定这与整个方法有什么关系,因为这似乎更像是框架的失败而不是任何事情。
  • 离开 JavaBean 火车?以这种方式进入 C# 平台...
  • 既然你提到了Wicket,我记得有这个:wicketwebbeans.sourceforge.net也许这对你有帮助?

标签: java properties javabeans


【解决方案1】:

我认为您与那里的声明非常接近(请参见下面的草图)。但是,通过使用非 bean 方法,您可能会失去大多数假定 JavaBeans 协议有效的工具所提供的支持。请善待。下面的代码是我的头顶...

public class Property<T> {
    public final String name;
    T value;
    private final PropertyChangeSupport support;

    public static <T> Property<T> newInstance(String name, T value, 
                                              PropertyChangeSupport support) {
        return new Property<T>(name, value, support);
    }

    public static <T> Property<T> newInstance(String name, T value) {
        return newInstance(name, value, null);
    }

    public Property(String name, T value, PropertyChangeSupport support) {
        this.name = name;
        this.value = value;
        this.support = support;
    }

    public T getValue() { return value; }

    public void setValue(T value) {
        T old = this.value;
        this.value = value;
        if(support != null)
            support.firePropertyChange(name, old, this.value);
    }

    public String toString() { return value.toString(); }
}

然后继续使用它:

public class Customer {
    private final PropertyChangeSupport support = new PropertyChangeSupport();

    public final Property<String> name = Property.newInstance("name", "", support);
    public final Property<Integer> age = Property.newInstance("age", 0, support);

    ... declare add/remove listenener ...
}


Customer c = new Customer();
c.name.setValue("Hyrum");
c.age.setValue(49);
System.out.println("%s : %s", c.name, c.age);

所以,现在声明一个属性是一行代码,并且包括属性更改支持。我调用了 setValue() 和 getValue() 方法,所以它看起来仍然像一个 bean 来编写 Rhino 之类的代码,但为了简洁起见,您可以只添加 get() 和 set()。剩下的留给读者练习:

  • 正确处理序列化
  • 处理空值检查
  • 如果您关心自动装箱开销,可以为原子类型添加专门化。
  • ??我敢肯定还有更多的陷阱

还请注意,您可以子类化(通常作为匿名类)并覆盖 setValue() 以提供额外的参数检查。

我认为您无法真正摆脱“字符串引用”,因为这几乎就是反射的全部内容。

遗憾的是,在这个时代,这仍然有点像汇编编程......如果你有选择的话,Groovy、C# 等可能仍然是更好的选择。

【讨论】:

    【解决方案2】:

    查看我的 Bean 注释

    http://code.google.com/p/javadude/wiki/Annotations

    基本上你会做这样的事情:

    @Bean(
      properties={
        @Property(name="name"),
        @Property(name="phone", bound=true),
        @Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
      }
    )
    public class Person extends PersonGen {}
    

    而不是自己定义所有这些额外的 get/set 等方法。

    还有其他属性可以定义equals/hashCode、observers、delegate、mixins等

    它是一组注解和一个注解处理器,可以在 eclipse 或命令行构建中运行(例如在 ant 中)。处理器生成一个超类来包含所有生成的代码(注解处理器无法更改包含注解的类,顺便说一句)

    【讨论】:

      【解决方案3】:

      您可能想查看Groovy - 一种动态类型、基于 JVM(且完全兼容 Java)的语言,具有“真实”属性。

      【讨论】:

        【解决方案4】:

        对于网络,我建议JSON(JavaScript Object Notation),

        一种轻量级的数据交换格式

        。这是对 JSON?Bean translator 的引用。

        【讨论】:

          【解决方案5】:

          使用 Spring 框架。它的目的是通过抽象您抱怨的大量基础工作来简化 Java 开发。

          您可以将它与 Hibernate 一起使用,这将为您简化与数据源的交互。

          有用的网站:

          www.springsource.org/下载

          www.hibernate.org/

          【讨论】:

            【解决方案6】:

            当我第一次使用 C# 时,我喜欢这些属性,但现在,在 VS 2008 中使用它们一段时间后,我不得不说我更喜欢 set-/get-methods。

            重点是我个人的工作方式。当我有一个新类并且我想知道我可以用它做什么时,我只需键入 classname.set,Eclipse 就会向我显示我可以更改哪些“属性”。 get 也是一样。也许它只是糟糕的 VS 方式,但是我必须浏览一长串这个 itelisense (所有东西都是混合的,而不是先显示属性),只是为了在我编译后发现我想要设置的属性是只读的。 ..doh!

            是的,在 Java 中您需要很多行,但我只是编写属性并告诉 IDE“请为我创建 getter 和 setter”。但是这些方法往往会占用大量空间,这就是为什么我希望在 Java 中也有可以在 IDE 中折叠的区域。

            【讨论】:

              【解决方案7】:

              您还可以构建一个代码生成器,从您编写的 DSL 创建您的 .java 类。您可以使用某种标记来描述您的类的名称、您想要的属性以及它们的类型。然后,使用生成 javabean 的程序处理该文件。或者您可以使用注释,并使用 ASM 之类的东西对类文件进行后处理,以注入访问器和修改器。我也相信 Spring 提供了其中一些功能,但我没有使用过。

              【讨论】:

              • 绝对是一种可行的方法,但我认为它错过了帖子的重点:他发现 JavaBeans 过于冗长和复杂。通过 DSL 或注释和后处理添加另一层不会让情况变得更糟吗?
              • @ian:DSL 可能不是一个坏主意,因为使用 DSL 往往会使事情变得简洁明了,正是发帖人的目标。
              • @ian:在短期内,我会同意,因为创建后处理器需要付出努力。但是,一旦完成,使用它的代码会按照要求的方式变得更简单。
              【解决方案8】:

              试试JBoss的SEAM框架,你应该会喜欢的。

              【讨论】:

                【解决方案9】:

                我试过这个:

                interface IListenable {
                    void addPropertyChangeListener( PropertyChangeListener listener );
                    void removePropertyChangeListener( PropertyChangeListener listener );
                }
                
                abstract class MyBean extends IListenable {
                    public abstract void setName(String name);
                    public abstract String getName();
                
                    // more things
                }
                
                public class JavaBeanFactory {
                
                   public <T> Class<T> generate(Class<T> clazz) {
                      // I used here CGLIB to generate dynamically a class that implements the methods:
                      // getters
                      // setters
                      // addPropertyChangeListener
                      // removePropertyChangeListener
                   }
                }
                

                我就是这样用的(这只是一个例子):

                public class Foo {
                    @Inject
                    public Provider<MyBean> myBeanProvider;
                
                    public MyBean createHook(MyBean a) {
                        final MyBean b  = myBeanProvider.get();
                        a.addPropertyChangeListener(new PropertyChangeListener() {
                             public void propertyChange(PropertyChangeEvent evt) {
                                 b.setName((String) evt.getNewValue());
                             }
                        });
                        return b;
                    }
                }
                

                【讨论】:

                  【解决方案10】:

                  我在模型实体(使用 JPA 注释)上大量使用 JavaBean 属性,以便能够将它们数据绑定到 UI(使用 JFace)。

                  很遗憾,我没有解决第二个问题的方法(也许除了定义包含属性名称的常量)。

                  我生成监听器样板的方法是让我的模型实体从一个使用反射处理它的 AbstractJavaBean 超类扩展。然后我可以使用创建getter/setter的默认方式,除了setter需要重写如下:

                  public void setRemarks(String remarks) {
                      set("remarks", remarks);
                  }
                  

                  AbstractJavaBean.set 然后使用反射(通过 Apache commons beanutils)通过其 getter 读取属性“remarks”的旧值,将新值设置为名为“remarks”的字段并使用旧值触发属性更改事件和新的价值观。事实上,这个想法可以扩展为使依赖的“派生”属性能够在属性之一基于更改时自动触发属性更改,例如在属性“birthDate”更改时更改“age”。所有这些逻辑都可以在 AbstractJavaBean 内的一个地方进行编码,并由任何模型对象使用。

                  【讨论】:

                    【解决方案11】:

                    我一直在寻找同样的东西,但我真的很惊讶我找不到合适的图书馆。由于我经常感到疼痛,因此我在 2 年前开始了一个解决这些问题的小项目:正确地

                    它可以在: https://github.com/aditosoftware/propertly。 我们现在在我们的产品中使用它。

                    它提供了带有泛型的静态类型、不同级别的侦听器、树导航、动态模型等等。 主要优点是模型的描述是静态完成的,因此信息始终可用,并且在设计时考虑了可扩展性。因此,您可以使用自己的注释进行验证或定义数据的读取和存储位置。

                    使用示例:

                    一个简单的 IPropertyPitProvider。属性是名字、姓氏和年龄。

                    // Generics describe parent, self and children
                    public class StudentPropertyPitProvider 
                        extends AbstractPPP<IPropertyPitProvider, StudentPropertyPitProvider, Object>
                    {
                      // IPropertyDescription gives static access to an IProperty's meta data like name and type.
                      public static final IPropertyDescription<StudentPropertyPitProvider, String> FIRST_NAME =
                          PD.create(StudentPropertyPitProvider.class);
                    
                      public static final IPropertyDescription<StudentPropertyPitProvider, String> LAST_NAME =
                          PD.create(StudentPropertyPitProvider.class);
                    
                      public static final IPropertyDescription<StudentPropertyPitProvider, Integer> AGE =
                          PD.create(StudentPropertyPitProvider.class);
                    
                    
                      // Getters and setters can of course still be used for easier access. 
                      public String getFirstName()
                      {
                        // getValue and setValue is available at AbstractPPP. That class is used for easier access. 
                        // Propertly can be used without inheriting from that class, too.
                        return getValue(FIRST_NAME);
                      }
                    
                      public void setFirstName(String pFirstName)
                      {
                        setValue(FIRST_NAME, pFirstName);
                      }
                    
                      public String getLastName()
                      {
                        return getValue(LAST_NAME);
                      }
                    
                      public void setLastName(String pLastName)
                      {
                        setValue(LAST_NAME, pLastName);
                      }
                    
                      public Integer getAge()
                      {
                        return getValue(AGE);
                      }
                    
                      public void setAge(Integer pAge)
                      {
                        setValue(AGE, pAge);
                      }
                    
                    }
                    

                    使用定义的提供者:

                    public class Sample
                    {
                    
                      public static void main(String[] args)
                      {
                        // Hierarchy is necessary to initialize the IPropertyPitProviders and for advanced features.
                        Hierarchy<StudentPropertyPitProvider> hierarchy =
                            new Hierarchy<>("student1", new StudentPropertyPitProvider());
                        // The created student can be accessed from the hierarchy.
                        StudentPropertyPitProvider student = hierarchy.getValue();
                        // Listeners can be added.
                        student.addPropertyEventListener(new PropertyPitEventAdapter()
                        {
                          @Override
                          public void propertyChanged(IProperty pProperty, Object pOldValue, Object pNewValue)
                          {
                            System.out.println(pProperty.getName() + "=" + pNewValue);
                          }
                        });
                    
                        // The following calls will cause
                        //  FIRST_NAME=Nils
                        //  LAST_NAME=Holgersson
                        //  AGE=32
                        // to be printed on console through the listener.
                        student.setFirstName("Nils");
                        student.setLastName("Holgersson");
                        student.setAge(32);
                      }
                    
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-11-08
                      • 2012-01-25
                      • 2015-08-05
                      • 2011-01-01
                      • 2011-10-24
                      • 2011-05-31
                      相关资源
                      最近更新 更多